它应该如何工作:父创建一个子节点,它执行第一个命令并将输出重定向到管道,然后第二个子节点将标准输入重定向到该管道。
我的问题:当第二个孩子试图从管道上读书时,它会永远等待
(我真的需要一个循环,因为在我的实际程序中,我不知道我将拥有多少命令,所以它只是一个简单的版本)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(int argc, char**argv) {
int fd[2];
pipe(fd);
for(int i = 0; i < 2; i++) {
if (fork() == 0) {
printf("\nloop %d\n", i);
if (i == 0) {
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
printf("\nbefore exec %s\n", argv[1]);
execlp(argv[1], argv[1], NULL);
}
else if(i == 1) {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
printf("\nbefore exec %s\n", argv[2]);
execlp(argv[2], argv[2], NULL);
}
exit(0);
}
wait(NULL);
}
return(0);
}
答案 0 :(得分:3)
除了无法检查函数调用的返回值以及处理可能由这些返回值指示的错误之外,您的代码看起来非常合理。但我确实看到了两个问题:
我看到的最重要的问题是主进程保持自己的管道末端副本打开。只要存在至少一个引用管道写入端的打开文件描述,在任何进程中,文件结尾都不会向读取端发送信号。如果在读取端运行的进程是读取其输入完成的进程(例如cat
,grep
或sed
,那么该进程将永远等待写入结束关闭,因为主要进程在该子进程终止之前不会退出(因为wait()
)。
主要过程在分叉后立即等待每个孩子,特别是在分叉之前等待第一个孩子。管道具有有限的缓冲区,如果第一个子节点在完成写入其所有输出之前填充管道缓冲区,则它将阻塞。在这种情况下,主进程永远不会启动第二个子进程,否则会导致管道耗尽。
总的来说,管道通常只适用于两个端点之间的通信。在通常情况下,重要的是在没有不必要的延迟的情况下启动读取器和写入器,并确保在所有进程中管道末端上的所有句柄都关闭,除了一个进程的一个线程中的读取端的一个副本,并且在不同的线程或不同的进程中写入一个副本。在像你这样的情况下,避免不必要的延迟包括wait()
任何子进程,直到两个(所有)孩子都已启动。