我想知道这个管道的实现有什么问题。我试图实现以下命令ls |grep "main-pipe" |wc
。然而,它进入了无限循环,我不明白为什么它不从标准输入中读取。我还检查了在第二个过程中是否正确获得了ls
的输出(对于grep
)是正确的,它是正确的。我不知道是什么导致了无限循环。你能帮我吗?
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
int main(int argc, char* argv[]) {
if (argc > 1) {
printf("Please don't provide additional arguments.\n");
exit(1);
}
int pipe1[2];
pipe(pipe1);
int pid = fork();
if (pid == 0) {
// Write to the write end of the pipe
close(1);
dup(pipe1[1]);
close(pipe1[1]);
close(pipe1[0]);
execlp("ls", "ls");
}
wait(0);
int pipe2[2];
pipe(pipe2);
pid = fork();
if (pid == 0) {
close(0);
dup(pipe1[0]);
close(pipe1[0]);
close(pipe1[1]);
close(1);
dup(pipe2[1]);
close(pipe2[0]);
close(pipe2[1]);
execlp("grep", "grep", "main-pipe", (char *)NULL);
}
wait(0);
close(0);
dup(pipe2[0]);
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
execl("wc", "wc");
exit(0);
}
字符串
1条答案
按热度按时间6tqwzwtp1#
它进入了无限循环,我不明白为什么它不从标准输入中读取。
无限循环并不是进程无法执行的唯一原因。没有理由认为任何进程无法尝试从其标准输入读取数据。在这种情况下,实际上,尝试从标准输入读取数据正是阻止
grep
进程执行的原因。您已经正确地执行了重定向,尽管正如我在评论中所指出的,这是
dup2()
比dup()
更好的选择。主要问题是
wait(0)
调用。这些调用在语义上是不合适的,因为它们干扰了shell风格管道的一个关键特性,即参与进程并行运行,每个进程在产生输出时或多或少地消耗前一个进程的输出。然而,更重要的是,wait()
s会给您带来实际问题,相当于进程间死锁。你需要欣赏几件事:
grep
在从其标准输入阅读时以这种方式工作。fork()
中重复而发生的。现在考虑一下这一切对您的程序的影响。
1.父节点设置一个管道,其端点存储在
pipe1
中。ls
,另一个运行grep
。wait()
。如果ls
的输出很长,那么这个wait()
可能会导致问题,管道的缓冲区填满了容量,因此阻止了ls
的完成。但这不太可能是在你的情况下实际发生的事情。ls
重定向其标准输出到管道的写入端,而grep
重定向是管道读取端的标准输入。ls
终止时,它所有打开的文件描述符都会自动关闭。这通常会允许grep
进程在其标准输入上观察到异常,从而自行终止。然而,这不会发生在您的程序中,因为父进程仍然有一个打开的文件描述符用于该管道的写入端。wait()
被阻塞。与前一个wait()
一样,如果grep
产生的输出足够大,可以填充其输出连接的管道的缓冲区,那么这个节点本身就可能是一个问题。然而,在这种情况下,这是没有意义的。因为父代推迟关闭其X1 M21 N1 X文件描述符的副本直到X1 M22 N1 X返回之后,而同时,grep
进程不能正常终止,直到父进程关闭该管道的写端。一旦grep
消耗了ls
的所有输出,两个进程都不能继续前进--它们被死锁了。你可以通过让父管道在派生第二个子管道时至少关闭管道1的写端来解决这个特殊的死锁,在这一点上,父管道对那个管道没有任何进一步的使用。尽快关闭不需要的管道端是一个很好的编程实践,几乎是必不可少的。但是消除
wait(0)
调用无论如何都是必要的,以避免我已经触及的其他问题,并且这样做将足以允许父节点足够快地执行其所有必要的管道端关闭以解决死锁。