以下代码用于执行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);
}
}
答案 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的任何读者(在这种情况下是孩子)将永远等待以防万一写更多内容将在其上完成......它不是,但读者只是不知道! - )