为啥父进程必须在调用wait()之前关闭管道的所有文件描述符?
Posted
技术标签:
【中文标题】为啥父进程必须在调用wait()之前关闭管道的所有文件描述符?【英文标题】:Why parent process has to close all file descriptors of a pipe before calling wait( )?为什么父进程必须在调用wait()之前关闭管道的所有文件描述符? 【发布时间】:2015-06-29 03:02:22 【问题描述】:不知道为什么父进程在调用wait()
之前需要关闭一个管道的两个文件描述符?
我有一个 C 程序:
-
Parent 创建 child_a,它使用 execvp 执行
ls -l
,并写入管道(在关闭管道的读取端之后)。
父级创建另一个子级(不关闭管道的任何文件描述符),称为 child_b,它通过从管道读取来执行“wc”。(在关闭管道的写入端之后)。
家长通过两次调用wait()
来等待两个孩子完成。
我注意到如果父进程在调用wait()
系统调用之前没有关闭管道的两个文件描述符,则程序被阻塞。此外,在阅读了一些已经在线发布的问题之后,看起来这是一般规则,需要完成。但我找不到必须这样做的原因?
如果父不关闭管道的文件描述符,为什么wait()
不返回?
我在想,在最坏的情况下,如果父级不关闭管道的文件描述符,那么唯一的后果就是管道将保持存在(这是一种资源浪费)。但我从没想过这会阻止子进程的执行(可以看出,因为wait()
没有返回)。
还要记住,父级根本没有使用管道。它是child_a
在管道中写入,child_b
从管道中读取。
【问题讨论】:
【参考方案1】:如果父进程不关闭管道的写入端,子进程永远不会获得 EOF(读取零字节),因为有一个进程可能(但不会)写入管道。出于同样的原因,子进程还必须关闭管道的写入端——如果不关闭,则有一个进程(本身)可能(但不会)写入管道,因此读取不会返回 EOF .
如果将管道的一端复制到标准输出或标准错误,则应关闭该管道的两端。在使用管道的多进程代码中没有对close()
进行足够的调用是一个常见错误。偶尔,你会因为马虎而侥幸逃脱,但细节会因情况而异,通常你不会。
【讨论】:
感谢父部分。不过,关于儿童部分的问题很少。我没有关闭child_a
中的写入端,但程序仍然有效(child_b 从管道读取,我可以在监视器中看到 child_b 的输出)。怎么会 ?是因为child_a
叫execvp( )
吗?
子A执行ls
;在标准输出上生成数据而不从标准输入中读取。当它完成生成数据时,它完成,并且管道描述符在它完成时关闭。但是,在您的场景中,子 B(又名 wc
)正在从其标准输入中读取。父进程仍然打开了作为子 B 标准输入的管道的写入端,因此 wc
永远不会获得 EOF 并且永远不会终止。 Child B 也有可能打开管道的写入端;这也意味着wc
永远不会得到EOF。以上是关于为啥父进程必须在调用wait()之前关闭管道的所有文件描述符?的主要内容,如果未能解决你的问题,请参考以下文章
UNIX中system函数的实现为啥要阻塞SIGCHLD信号?
为啥在调用 c++ fork 函数之前创建的值没有被父进程和子进程修改两次?