子进程被满管道阻塞,无法读取父进程

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

本文关键字:读取 进程 满管道 管道 子进程      更新时间:2023-10-16

我大致创建了以下代码来调用子进程:

// 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]));
  }

现在,如果子进程没有向标准输出写入太多内容,我可以等待它完成,然后从p_stdout读取标准输出。如果写入太多,则写入阻塞,父进程将永远等待它。为了解决这个问题,我试图在父进程中等待WNOHANG,如果它没有完成,使用readsome从p_stdout读取所有可用的输出,睡一点,然后再试一次。不幸的是,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);
  }

问题是:为什么这不能工作,我如何解决它?

edit:如果只有子进程,简单地使用read而不是readsome可能会工作,但是该进程有多个子进程,并且需要在其中一个终止时立即做出反应。

正如sarnold建议的那样,您需要更改调用的顺序。先阅读,后等待。即使你的方法有效,你也可能错过最后的阅读。也就是说,在读取最后一组写入的字节之前退出循环。

问题可能是ifstream是非阻塞的。我从来都不喜欢iostreams,即使在我的c++项目中,我总是喜欢C的studio函数的简单性(例如FILE*, fprintf等)。解决这个问题的一种方法是读取描述符是否可读。您可以使用select来确定该管道上是否有数据等待。如果你要从多个子节点读取数据,你将需要select,所以最好现在就学习它。

对于一个快速的可读函数,尝试这样做(请注意,我没有尝试编译这个):

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;
}

然后在父代码中,做如下操作:

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

我建议重写代码,直到在管道上调用 read(2)返回0以表示文件结束时,它才调用waitpid(2)。一旦你从你的read调用中得到文件结束返回值,你就知道子进程已经死亡了,你最终可以为它waitpid(2)了。

另一种选择是进一步解除读取与收获的耦合,并在SIGCHLD信号处理程序中异步执行等待调用。