学习pipes、exec、fork,并尝试将三个流程链接在一起
我正在学习使用管道,并遵循上面的代码。该程序使用fork生成两个子进程。第一个子级运行“ls”命令并输出到pipe1。第二个从pipe1读取的数据运行“wc”,并输出到stdout 我试图在中间添加第三个过程,从PIPE1读取并输出到PIPE2。基本上就是我想做的学习pipes、exec、fork,并尝试将三个流程链接在一起,c,linux,exec,fork,pipe,C,Linux,Exec,Fork,Pipe,我正在学习使用管道,并遵循上面的代码。该程序使用fork生成两个子进程。第一个子级运行“ls”命令并输出到pipe1。第二个从pipe1读取的数据运行“wc”,并输出到stdout 我试图在中间添加第三个过程,从PIPE1读取并输出到PIPE2。基本上就是我想做的 ls | cat | wc -l 我想做的是: (ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout 任何东西都不会打印到标准
ls | cat | wc -l
我想做的是:
(ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout
任何东西都不会打印到标准输出,程序也不会退出
这是我对流程3所做更改的代码
问题是您没有在流程3中关闭
pfd[1]
,添加close(pfd[1])代码>在该过程中的情况0之后,3将修复它
在过程3中,cat
将从pfd[0]
读取,但是在这些过程中有四个pfd[1]
:
进程0
这是主进程,pfd[1]
在此进程中,将在wait()
之前关闭该进程
过程1
在ls
完成后,操作系统将自动关闭此过程中的pfd[1]
过程2
pfd[1]
在执行cat
之前已关闭
过程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);
}
pfd[1]
在wc
运行时在此过程中打开,这就是当时发生的情况:
在过程2中,cat
尝试从pfd[1]
读取数据
在过程3中,wc
尝试从pfd2[1]
读取数据
由于进程3中的pdf[1]
仍处于打开状态,并且不会写入任何内容,因此从进程2(cat)中的pfd[0]
读取将永远等待
由于进程3中的cat
仍处于活动状态,因此从进程3(wc)中的pfd2[0]
读取将等待(永远)
如您所见,由于文件描述符泄漏,进程2(cat)和进程3(wc)之间存在死锁。要打破此死锁,只需在运行wc
之前关闭流程3中的pfd[1]
,然后:
进程2中的cat
将在进程1中的ls
退出后退出,因为没有剩余内容可供其(cat)读取
在进程2中的cat
退出后,进程3中的wc
也将退出,因为它(wc)已无可读取的内容
之后,主进程(父进程)将退出,程序将完成
管道的读端可能有多个写端,除非所有这些写端都关闭,否则文件的结尾不会传递到读端,读卡器只会等待更多数据的到来。如果没有什么事情发生,读者将永远等待。这解决了问题,谢谢。但我仍然不确定它为什么起作用。我想我需要更多地了解一下管道的工作原理。进程3的stdin或stdout都不使用pfd,因此我认为不需要关闭它。@JDD管道的读取端可能有多个写入端,除非所有这些写入端都关闭,否则文件的结尾将不会传递到读取端,而读取器将只等待更多数据的到来。如果没有什么事情发生,这就是你的情况,读者将永远等待。我明白了。我不知道他们是那样工作的。谢谢+1,但我认为一个更干净的解决方案是在使用管道的进程分支之后关闭默认情况下的管道(父级)。通常,创建尽可能靠近fork的管道,并尽快将其关闭;它不会退出。您的案例语句将报告错误,然后进入下一个案例,这不是您想要的。