简体   繁体   English

子进程被完整管道阻塞,无法在父进程中读取

[英]Child process is blocked by full pipe, cannot read in parent process

I have roughly created the following code to call a child process: 我粗略地创建了以下代码来调用子进程:

// pipe meanings
const int READ = 0;
const int WRITE = 1;

int fd[2];
// Create pipes
if (pipe(fd))
  {
    throw ...
  }
p_pid = fork();
if (p_pid == 0) // in the child
  {
    close(fd[READ]);
    if (dup2(fd[WRITE], fileno(stdout)) == -1)
      {
        throw ...
      }
    close(fd[WRITE]);

    // Call exec
    execv(argv[0], const_cast<char*const*>(&argv[0]));
    _exit(-1);
  }
else if (p_pid < 0) // fork has failed
  {
    throw
  }
else // in th parent
  {
      close(fd[WRITE]);
      p_stdout = new std::ifstream(fd[READ]));
  }

Now, if the subprocess does not write too much to stdout, I can wait for it to finish and then read the stdout from p_stdout . 现在,如果子进程没有对stdout写太多,我可以等待它完成,然后从p_stdout读取stdout。 If it writes too much, the write blocks and the parent waits for it forever. 如果写得太多,写块和父块将永远等待它。 To fix this, I tried to wait with WNOHANG in the parent, if it is not finished, read all available output from p_stdout using readsome , sleep a bit and try again. 为了解决这个问题,我尝试在父母中等待WNOHANG ,如果没有完成,请使用readsome读取p_stdout的所有可用输出,稍微睡一会然后再试一次。 Unfortunately, readsome never reads anything: 不幸的是, readsome从不读任何东西:

while (true)
  {
    if (waitid(P_PID, p_pid, &info, WEXITED | WNOHANG) != 0)
      throw ...;
    else if (info.si_pid != 0) // waiting has succeeded
      break;

    char tmp[1024];
    size_t sizeRead;
    sizeRead = p_stdout->readsome(tmp, 1024);
    if (sizeRead > 0)
      s_stdout.write(tmp, sizeRead);
    sleep(1);
  }

The question is: Why does this not work and how can I fix it? 问题是:为什么这不起作用,我该如何解决?

edit: If there is only child, simply using read instead of readsome would probably work, but the process has multiple children and needs to react as soon as one of them terminates. 编辑:如果只有孩子,只需使用read而不是readsome可能会有效,但该过程有多readsome进程,并且只要其中一个终止就需要做出反应。

As sarnold suggested, you need to change the order of your calls. 正如sarnold建议的那样,您需要更改通话顺序。 Read first, wait last. 先读,等等。 Even if your method worked, you might miss the last read. 即使您的方法有效,您也可能会错过最后一次阅读。 ie you exit the loop before you read the last set of bytes that was written. 即,在读取写入的最后一组字节之前退出循环。

The problem might be is that ifstream is non-blocking. 问题可能是ifstream是非阻塞的。 I've never liked iostreams, even in my C++ projects, I always liked the simplicity of C's stdio functions (ie FILE*, fprintf, etc). 我从来都不喜欢iostream,即使在我的C ++项目中,我总是喜欢C的stdio函数的简单性(即FILE *,fprintf等)。 One way to get around this is to read if the descriptor is readable. 解决这个问题的一种方法是读取描述符是否可读。 You can use select to determine if there is data waiting on that pipe. 您可以使用select来确定是否有数据在该管道上等待。 You're going to need select if you are going to read from multiple children anyway, so might as well learn it now. 如果你打算从多个孩子那里读书,你将需要选择,所以不妨现在就学习它。

As for a quick isreadable function, try something like this (please note I haven't tried compiling this): 至于一个快速可读的功能,尝试这样的事情(请注意我还没有尝试编译这个):

bool isreadable(int fd, int timeoutSecs)
{
    struct timeval tv = { timeoutSecs, 0 };
    fd_set readSet;
    FD_ZERO(&readSet);
    return select(fds, &readSet, NULL, NULL, &tv) == 1;
}

Then in your parent code, do something like: 然后在您的父代码中,执行以下操作:

while (true) {
    if (isreadable(fd[READ], 1)) {
        // read fd[READ];
        if (bytes <= 0)
            break;
    }
}

wait(pid);

I'd suggest re-writing the code so that it doesn't call waitpid(2) until after read(2) calls on the pipe return 0 to signify end-of-file. 我建议重新编写代码,以便它不调用waitpid(2)直到 read(2)在管道返话费0来表示结束文件。 Once you get the end-of-file return from your read calls, you know the child is dead, and you can finally waitpid(2) for it. 一旦从读取调用中获得文件结束返回,您就知道孩子已经死了,您最终可以等待它waitpid(2)

Another option is to de-couple the reading from the reaping even further and perform the wait calls in a SIGCHLD signal handler asynchronously to the reading operations. 另一种选择是将读取与收获进一步解耦,并在SIGCHLD信号处理程序中异步执行等待调用,以与读取操作异步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM