为啥父进程必须在调用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_aexecvp( )吗? 子A执行ls;在标准输出上生成数据而不从标准输入中读取。当它完成生成数据时,它完成,并且管道描述符在它完成时关闭。但是,在您的场景中,子 B(又名 wc)正在从其标准输入中读取。父进程仍然打开了作为子 B 标准输入的管道的写入端,因此 wc 永远不会获得 EOF 并且永远不会终止。 Child B 也有可能打开管道的写入端;这也意味着wc 永远不会得到EOF。

以上是关于为啥父进程必须在调用wait()之前关闭管道的所有文件描述符?的主要内容,如果未能解决你的问题,请参考以下文章

函数wait和waitpid

UNIX中system函数的实现为啥要阻塞SIGCHLD信号?

为啥在调用 c++ fork 函数之前创建的值没有被父进程和子进程修改两次?

linux处理僵尸进程

如果父级不调用 wait(),则同一父级的两个子级不使用管道进行通信

在这种情况下使用管道会导致 write() 失败吗?