最常见的原因是您没有关闭管道的写入端口,因此EOF永远不会被发送。常见的示例是当您的代码看起来像这样时:
int fds[2];
pipe(fds);
if (fork()) {
write(fds[1], ...
close(fds[1]);
} else {
while (read(fds[0], ....) > 0) {
这里的问题是管道的写入端从未关闭——父进程关闭了它,但子进程仍然保持着写入描述符的开启。所以子进程在读取描述符上永远不会看到EOF。
在fork出子进程后,第一件事就是要关闭它的写描述符(close(fds[1]);),关闭其写入描述符的副本。这样,当父进程关闭对管道写入端的最后一个引用时,子进程将在读取端看到EOF。
编辑:
查看您添加的链接,这正是问题所在——子进程仍然在其stdout上保持着管道的写入端。在子进程中不要复制写入端到stdout,只需将其关闭即可。将stdout发送到其他地方(日志文件或/dev/null)。
编辑:
对于双向通信,您需要两个管道:
int tochild[2], fromchild[2];
pipe(tochild); pipe(fromchild);
if (fork()) {
close(tochild[0]);
close(fromchild[1]);
} else {
dup2(tochild[0], 0);
dup2(fromchild[1], 1);
close(tochild[0]); close(tochild[1]);
close(fromchild[0]); close(fromchild[1]);
exec(...
}
在向子进程发送大量数据时,您需要非常小心地编写父进程的数据。否则,如果在读取子进程的输出之前发送了所有数据,则可能会发生死锁(两个管道都被填满,父进程阻塞尝试为子进程写入更多数据,而子进程则阻塞尝试输出)。您需要使用poll或select来告诉何时有数据可读或空间可写,并且您可能希望将管道(至少是父端)设置为非阻塞模式。