以下代码用于执行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);
    }
}
有帮助吗?

解决方案

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

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

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

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

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

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

其他提示

请注意,' 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:

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top