我想知道这个管道的实现有什么问题.我正在try 实现以下命令ls |grep "main-pipe" |wc.然而,它进入了无限循环,我不明白为什么它不能从标准输入中读取.我还判断了在第二个过程中(对于grep)从ls获得的输出是否正确以及它是否正确.我想不出是什么导致了无限循环.你能帮我拿一下吗?

#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);
}

推荐答案

它进入无限循环,我不明白为什么它不能从标准输入中读取.

无限循环并不是未能取得进展的唯一原因.没有理由认为任何进程都没有try 从其标准输入中读取.在这种情况下,实际上,试图从标准输入中读取正是阻止grep进程取得进展的原因.

您已经正确地执行了重定向,尽管如我在 comments 中所指出的,这是dup2()dup()更好的 Select 的许多情况之一.

主要的问题是你的wait(0)个电话.这些在语义上是不合适的,因为它们干扰了shell 式管道的一个关键特征,即参与的进程并行运行,每个进程在生成时或多或少地消耗前一个进程的输出.然而,更重要的是,wait()给您带来了一个实际问题,相当于进程间的死锁.

你需要欣赏几件事:

  • 作为UNIX风格过滤器运行的进程从其标准输入读取和处理数据,直到它检测到文件结束. 通常,这样的流程将其结果发送到其标准输出,从而允许组合多个这样的流程. grep在从其标准输入读取时以这种方式工作.

  • 只有在管道的写入端的所有副本都已关闭之后,才能在该管道的读取端观察到EOF.在那之前,读者永远不能确信最终不会有更多的数据需要阅读.

  • 每个进程都有自己的文件描述符,但多个进程都可以有自己的文件描述符,这些文件描述符引用相同的底层打开文件描述.这些FDs的数值有时是相同的,但它们不一定是相同的.这种对相同打开文件描述的多次引用尤其是由于文件描述符跨fork()被复制而发生的.

现在考虑一下这一切在您的程序中是如何进行的.

  1. 父级设置其末端存储在pipe1中的管道.

  2. 它Forking 了两个子元素,一个跑ls,另一个跑grep. 这些文件中的每一个都继承管道末端的父文件描述符的副本.

    • 我忽略了这里的前wait()个.如果ls‘S的yields 很长,那么这个wait()可能会造成问题,管道的缓冲区填满了容量,从而阻止了ls的完成.但这不太可能是你的实际情况.
  3. 每个子级执行适当的重定向.ls将其标准输出重定向到管道的写入端,而grep重定向是来自管道读取端的标准输入.

  4. 这些子 node 中的每一个关闭its own excess copies该管道末端的文件描述符.然而,在这一点上,the parent still has open file descriptors for both ends of that pipe.

  5. ls终止时,其所有打开的文件描述符都将自动关闭.这通常允许grep进程在其标准输入上观察EOF,从而使其自身终止.然而,在您的程序中不会发生这种情况,因为父级仍然有一个打开的文件描述符,用于该管道的写入端.

  6. 与此同时,父母在第二个wait()被挡住了.与前面的wait()一样,如果grep产生足够大的输出来填充其输出所连接的管道的缓冲区,那么严格地说,这个问题本身就是一个问题.然而,在本例中这是没有意义的,因为父进程将关闭pipe1个文件描述符的副本推迟到wait()返回之后,而与此同时,grep进程在父进程关闭该管道的写入端之前不能正常终止.一旦grep消耗了ls的所有输出,这两个进程都无法取得进展--它们处于死锁状态.

您可以通过让父进程在派生第二个子进程时至少关闭pipe1的写入端来解决这个特定的死锁,此时父进程无论如何都不再使用该管道.尽快关闭不需要的管道末端是很好的编程实践,几乎是必不可少的.但是无论如何,为了避免我提到的其他问题,消除wait(0)个调用是必要的,这样做就足以让父进程足够快地执行其所有必要的管道末端关闭,以解决死锁.

C++相关问答推荐

生成C代码时自动复制/生成' tmwtypes.h '依赖项

如何避免使用相对路径包含在c中

ATmega328P USART发送字符重复打印

以c格式打印时间戳

如何调试LD_PRELOAD库中的构造函数?

C lang:当我try 将3个或更多元素写入数组时,出现总线错误

将uintptr_t添加到指针是否对称?

VS代码';S C/C++扩展称C23真关键字和假关键字未定义

X64:并发写入布尔数组

为什么我可以在GCC的标签后声明变量,但不能声明Clang?

S在本文中的价值观到底出了什么问题?

指向不同类型的指针是否与公共初始序列规则匹配?

将变量或参数打包到 struct /联合中是否会带来意想不到的性能损失?

收到不兼容的指针类型警告,因为函数的返回不是空*,而是 struct 指针

初始成员、公共初始序列、匿名联合和严格别名如何在C中交互?

从另一个宏函数调用C宏

C:面筋蛋白';为什么不刷新窗口?

memcmp 是否保证按顺序比较字节?

如何确定 C 程序中的可用堆内存

我怎样才能用c语言正常运行这两个进程?