对Unix管道的疑问

时间:2009-09-15 05:29:00

标签: unix pipe

以下代码用于执行ls -l | wc -l。 在代码中,如果我在父级中注释close(p [1]),那么程序就会挂起,等待一些输入。为什么会这样?子项在p1上写入ls的输出,父项应从p0中获取该输出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

main ()
{
  int i;
  int p[2];
  pid_t ret;
  pipe (p);
  ret = fork ();

  if (ret == 0)
    {
      close (1);
      dup (p[1]);
      close (p[0]);
      execlp ("ls", "ls", "-l", (char *) 0);
    }

  if (ret > 0)
    {
      close (0);
      dup (p[0]);
      //Doubt, Commenting the line below does not work WHy?
      close (p[1]);
      wait (NULL);
      execlp ("wc", "wc", "-l", (char *) 0);
    }
}

3 个答案:

答案 0 :(得分:4)

pipe + fork创建4个文件描述符,其中两个是输入

在fork之前,你有一个带有一个输入和一个输出的管道。

在叉子之后,您将拥有一个带有两个输入和两个输出的管道。

如果管道有两个输入(proc写入)和两个输出(proc读取),则需要关闭另一个输入,或者读取器也有一个永不关闭的管道输入。

在您的情况下,父级是读者,除了管道的输出端之外,它还有一个开放的另一端或输入端,在理论上可以,写到。因此,管道永远不会发送eof,因为当子管退出管道时,由于父管道未使用的fd,它仍处于打开状态。

所以父母陷入僵局,等待它永远写下来。

答案 1 :(得分:3)

请注意,“dup(p[1])”表示您有两个指向同一文件的文件描述符。它没有关闭p[1];你应该明确地这样做。与'dup(p[0])'相同。请注意,当管道没有打开的写文件描述符时,从管道读取的文件描述符仅返回零字节(EOF);直到最后一个写入描述符关闭,读取过程将无限期挂起。如果dup()写入结束,则写入端有两个打开的文件描述符,并且必须在读取过程获得EOF之前关闭它们。

您也不需要或希望在代码中进行wait()调用。如果ls列表大于管道可以容纳的内容,则您的进程将陷入死锁状态,并且子进程等待ls完成,ls等待孩子继续阅读数据它写了。

当剥离冗余材料时,工作代码变为:

#include <unistd.h>

int main(void)
{
    int p[2];
    pid_t ret;
    pipe(p);
    ret = fork();

    if (ret == 0)
    {
        close(1);
        dup(p[1]);
        close(p[0]);
        close(p[1]);
        execlp("ls", "ls", "-l", (char *) 0);
    } 
    else if (ret > 0)
    {
        close(0);
        dup(p[0]);
        close(p[0]);
        close(p[1]);
        execlp("wc", "wc", "-l", (char *) 0);
    }
    return(-1);
}

在Solaris 10上,编译时没有警告:

Black JL: gcc -Wall -Werror -Wmissing-prototypes -Wstrict-prototypes -o x x.c
Black JL: ./x
      77
Black JL:

答案 2 :(得分:0)

如果孩子没有关闭p[1],则该FD在两个过程中开放 - 父母和孩子。父母最终关闭它,但孩子从来没有 - 所以FD保持开放。因此,FD的任何读者(在这种情况下是孩子)将永远等待以防万一写更多内容将在其上完成......它不是,但读者只是不知道! - )