为什么要关闭linux中的管道?

时间:2013-10-09 07:00:54

标签: c linux pipe

当使用管道进行过程 - 过程通信时,关闭管道一端的目的是什么?

例如:How to send a simple string between two programs using pipes?

请注意,管道的一侧在子进程和父进程中是关闭的。为什么需要这个?

3 个答案:

答案 0 :(得分:39)

如果使用管道连接两个进程(父进程和子进程),则在fork之前创建管道。

fork使两个进程都可以访问管道的两端。这是不可取的。

如果注意到EOF条件,那么阅读方应该知道作者已经完成了。这只有在所有书写边都关闭的情况下才会发生。因此,最好尽快关闭其写作FD。

作者应该关闭其阅读FD,以便不会有太多的FD开放,从而达到可能存在的开放FD限制。此外,如果当时唯一的读取器死亡,则通过获取SIGPIPE或至少EPIPE错误(取决于如何定义信号)来通知写入者。如果有几个读者,那么作者就无法检测到“真正的”读者会离开,继续写作并因为写作FD阻塞而陷入困境,“未使用”的读者会读到一些东西。

所以详细说明会发生什么:

  • 父进程调用pipe()并获取2个文件描述符:我们称之为rdwr
  • 父进程调用fork()。现在,这两个流程都有rdwr
  • 假设子进程应该是读者。

    然后

    • 父母应该关闭其阅读结束(不浪费FD并正确检测垂死的读者)和
    • 孩子必须关闭其书写结束(以便能够检测到EOF条件)。

答案 1 :(得分:5)

在给定时间可以打开的文件描述符的数量是有限的。如果你继续打开管道而不是很快关闭它们你就会耗尽FD并且不能打开任何东西:不是管道,不是文件,不是插座,......

关闭管道可能很重要的另一个原因是关闭本身对应用程序有意义。例如,管道的常见用法是在使用errnoforklaunch an external program时将exec从子进程发送到父进程:

  1. 父级创建管道,调用fork创建子进程,关闭其写入结束,并尝试从管道读取。
  2. 子进程尝试使用exec运行其他程序:
    1. 如果exec失败,例如因为程序不存在,则孩子将errno写入管道,父母读取它并知道出了什么问题,并告诉用户。
    2. 如果exec成功,管道将关闭而不会写入任何内容。父项中的read函数返回0,表示管道已关闭,并且知道程序已成功启动。
  3. 如果父级在尝试从管道读取之前没有关闭管道的写入端,那么这将无效,因为read函数在exec成功时永远不会返回。

答案 2 :(得分:0)

关闭未使用的管道文件描述符不仅仅是确保进程不耗尽其有限的文件描述符集,这对于正确使用管道至关重要。现在我们考虑为什么必须关闭管道的读取和写入端的未使用文件描述符。 从管道读取的进程关闭了其管道的写入描述符,因此,当另一个进程完成其输出并关闭其写入描述符时,读取将看到文件结尾(一旦它已准备好管道中任何未完成的数据)。 。 如果读取过程没有关闭管道的写入端,那么在另一个进程关闭其写入描述符之后,即使读取器已经从管道读取了所有数据,读取器也不会看到文件结尾。相反,read()会阻止等待数据,因为内核知道至少还有一个写描述符要为管道打开。该描述符由读取过程本身保持打开状态是无关紧要的。从理论上讲,即使该过程被阻止尝试读取,该过程仍然可以写入管道。 例如,read()可能会被将数据写入管道的信号处理程序中断。 写入过程出于不同的原因而关闭了管道的读取描述符。 当某个进程试图写入没有进程具有打开的读取描述符的管道时,内核将SIGPIPE信号发送到写入进程。默认情况下,此信号会终止进程。相反,进程可以安排捕获或忽略此信号,在这种情况下,管道上的write()失败,并显示错误EPIPE(管道断开)。收到SIGPIPE信号或得到EPIPE错误是有关管道状态的有用指示,这就是为什么应关闭管道未使用的读取描述符的原因。 如果写入过程没有关闭管道的读取端,那么即使在另一个进程关闭了管道的读取端之后,写入过程也会填满管道,并且进一步的写入尝试将无限期地阻塞。 关闭未使用的文件描述符的最后一个原因是,只有在关闭所有文件描述符后,管道才被销毁,其资源被释放以供其他进程重用。此时,管道中所有未读的数据都会丢失。

〜Linux编程接口Micheal Kerrisk