我想知道这个管道的实现有什么问题。我正在尝试执行以下命令
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);
}
它进入无限循环,我不明白为什么它不从标准输入读取。
无限循环并不是无法取得进展的唯一原因。没有理由认为任何进程都无法尝试读取其标准输入。事实上,在这种情况下,尝试从标准输入读取正是阻止
grep
进程取得进展的原因。
您已经正确执行了重定向,尽管正如我在评论中指出的那样,这是许多情况下
dup2()
比 dup()
更好的选择。
主要问题是您的
wait(0)
电话。这些在语义上是不合适的,因为它们干扰了 shell 式管道的关键特征之一,即参与进程并行运行,每个进程在生成时或多或少地消耗前一个进程的输出。然而,更重要的是,wait()
会给您带来一个实际问题,相当于进程间死锁。
你需要欣赏几件事:
作为 UNIX 样式过滤器运行的进程从其标准输入读取并处理数据,直到检测到文件结尾。通常,这样的进程将其结果发送到其标准输出,从而允许组合多个这样的进程。
grep
从标准输入读取时以这种方式工作。
fork()
中重复而发生。
pipe1
中。
ls
,另一个运行
grep
。其中每个都继承了管道末端的父级文件描述符的副本。
我在这里忽略第一个wait()
ls
的输出很长,那么这个 wait()
可能会导致问题,管道的缓冲区已满,从而阻止 ls
完成。但这不太可能是您的案例中实际发生的情况。
ls
将其标准输出重定向到管道的写入端,而
grep
重定向是来自管道的读取端的标准输入。
父管道的两端仍然有打开的文件描述符。
当ls
grep
进程观察其标准输入上的 EOF,从而自行终止。然而,这在您的程序中不会发生,因为父级仍然有一个用于该管道写入端的打开文件描述符。同时,父级在第二个
wait()
wait()
一样,如果
grep
产生足够大的输出来填充其输出所连接的管道的缓冲区,那么这本身就可能是一个问题。然而,在这种情况下,这是没有意义的,因为父进程推迟关闭其
pipe1
文件描述符的副本,直到
wait()
返回之后,而同时,
grep
进程无法正常终止,直到父进程关闭写操作为止。该管子的末端。一旦
grep
消耗了
ls
的所有输出,两个进程都无法取得进展——它们陷入僵局。您可以通过让父级在分叉第二个子级时至少关闭 pipeline1 的写入端来解决这个特定的死锁,此时父级无论如何都不再使用该管道。尽快关闭不需要的管端是良好的编程习惯,几乎是必不可少的。但是无论如何,消除
wait(0)