我正在学习使用管道,并在pipes上跟随这段代码。程序使用fork生成两个子进程。第一个子进程运行'ls'命令并输出到pipe1。第二次从pipe1读取运行'wc'并输出到stdout。
我只想在中间添加第三个进程,从pipe1读取并输出到pipe2。基本上我想做的就是
ls | cat | wc -l
我要做的是:
(ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout
从来没有打印到STDUT,程序永远不会退出。
这是我的代码和流程3的更改
int
main(int argc, char *argv[])
{
int pfd[2]; /* Pipe file descriptors */
int pfd2[2];
if (pipe(pfd) == -1) /* Create pipe */
perror("pipe");
if (pipe(pfd2) == -1) /* Create pipe */
perror("pipe");
/*
Fork process 1 and exec ls command
write to pfd[1], close pfd[0]
*/
switch (fork()) {
case -1:
perror("fork");
case 0:
if (close(pfd[0]) == -1)
perror("close 1");
// dup stdout on pfd[1]
if (pfd[1] != STDOUT_FILENO) {
if (dup2(pfd[1], STDOUT_FILENO) == -1)
perror("dup2 2");
if (close(pfd[1]) == -1)
perror("close 4");
}
execlp("ls", "ls", (char *) NULL);
perror("execlp ls");
default:
break;
}
/*
* Fork process 2 and exec wc command
read from pfd[0], close pfd[1]
write to pfd[1], close pfd2[0]
*/
switch (fork()) {
case -1:
perror("fork");
case 0:
// read from pfd[0]
if (close(pfd[1]) == -1)
perror("close 3");
if (pfd[0] != STDIN_FILENO) {
if (dup2(pfd[0], STDIN_FILENO) == -1)
perror("dup2 2");
if (close(pfd[0]) == -1)
perror("close 4");
}
if (pfd2[1] != STDOUT_FILENO) {
if (dup2(pfd2[1], STDOUT_FILENO) == -1)
perror("dup2 2");
if (close(pfd2[1]) == -1)
perror("close 4");
}
execlp("cat", "cat", (char *) NULL);
perror("execlp cat");
default:
break;
}
/*
* Fork process 3
*/
switch (fork()) {
case -1:
perror("fork");
case 0:
if (close(pfd2[1]) == -1)
perror("close 3");
if (pfd2[0] != STDIN_FILENO) {
if (dup2(pfd2[0], STDIN_FILENO) == -1)
perror("dup2 2");
if (close(pfd2[0]) == -1)
perror("close 4");
}
execlp("wc", "wc", "-l", (char *) NULL);
perror("execlp wc");
default:
break;
}
/* Parent closes unused file descriptors for pipe, and waits for children */
if (close(pfd[0]) == -1)
perror("close 5");
if (close(pfd[1]) == -1)
perror("close 6");
if (close(pfd2[0]) == -1)
perror("close 5");
if (close(pfd2[1]) == -1)
perror("close 6");
if (wait(NULL) == -1)
perror("wait 1");
if (wait(NULL) == -1)
perror("wait 2");
if (wait(NULL) == -1)
perror("wait 3");
exit(EXIT_SUCCESS);
}
最佳答案
问题是您没有在流程3中关闭pfd[1]
,在流程3中的案例0之后添加close(pfd[1]);
将修复它。
在过程3中,cat
将从pfd[0]
读取,但是在这些过程中有四个pfd[1]
:
进程0
这是主进程,pfd[1]
在此进程中将在wait()
之前关闭。
流程1
完成ls
后,操作系统将自动关闭此过程中的pfd[1]
。
过程2pfd[1]
在执行cat
之前已关闭。
过程3pfd[1]
在此进程中处于打开状态,而wc
正在运行,这就是当时发生的情况:
在过程2中,cat
尝试读取pfd[0]
中的数据。
在过程3中,pfd[1]
尝试读取wc
中的数据。
因为pfd2[0]
在进程3中仍然打开,并且不会写入任何内容,所以从进程2中的pfd2[1]
读取(cat)将永远等待
因为进程3中的pdf[1]
仍然有效,所以从进程3中的pfd[0]
读取(wc)将等待(永远)
如您所见,由于文件描述符泄漏,进程2(cat)和进程3(wc)之间存在死锁。要打破此死锁,只需在运行cat
之前关闭进程3中的pfd2[0]
,然后:
过程2中的pfd[1]
将在过程1退出后wc
退出,因为它(CAT)没有任何内容可读。
在过程2退出cat
之后,过程3中的ls
也将退出,因为它(WC)没有剩下什么可以读取。
之后,主进程(父进程)将退出,程序将结束。
管道的读端可能有多个写端,除非所有这些写端都已关闭,否则文件端将不会传递到读端,读取器将只等待更多数据的到来。如果什么都没有,那读者将永远等待。