glg*_*lgl 43
如果使用管道连接两个进程(父进程和子进程),则在fork之前创建管道.
fork使两个进程都可以访问管道的两端.这是不可取的.
阅读方应该知道如果注意到EOF条件,则作者已完成.这只有在所有书写边都关闭的情况下才会发生.因此,最好尽快关闭其写作FD.
作者应该关闭其阅读FD,以便不会有太多的FD开放,从而达到可能存在的开放FD限制.此外,如果当时唯一的读取器死亡,则通过获取SIGPIPE或至少EPIPE错误(取决于如何定义信号)来通知写入者.如果有几个读者,作者无法检测到"真实的"消失了,继续写作并因为写作FD阻塞而陷入困境,"未使用"的读者会读到一些东西.
所以详细说明会发生什么:
pipe(),并得到2个文件描述符:让我们把它rd和wr.fork().现在这两个进程都有a rd和a wr.假设子进程应该是读者.
然后
在给定时间可以打开的文件描述符的数量是有限的.如果你不断打开管道而不是很快就关闭它们就会耗尽FD并且不能打开任何东西:不是管道,不是文件,不是插座,......
关闭管道可能很重要的另一个原因是关闭本身对应用程序有意义.例如,管道的一个常见的用途是发errno用时从一个子进程的父fork和exec以启动一个外部程序:
fork创建子进程,关闭其写入结束,并尝试从管道读取.exec运行不同的程序:
exec失败,例如因为程序不存在,则子进程写入errno管道,并且父进程读取并知道出了什么问题,并且可以告诉用户.exec成功,则管道关闭而不写入任何内容.read父级中的函数返回0,表示管道已关闭,并且知道程序已成功启动.如果父级在尝试从管道读取之前没有关闭管道的写入端,则这将无效,因为该read函数在exec成功时永远不会返回.
小智 8
关闭未使用的管道文件描述符不仅仅是确保进程不会耗尽其有限的文件描述符集,这对于正确使用管道至关重要。我们现在考虑为什么必须关闭管道读取端和写入端未使用的文件描述符。从管道读取的进程会关闭其管道的写入描述符,因此,当另一个进程完成其输出并关闭其写入描述符时,读取会看到文件结尾(一旦它准备好管道中的任何未完成的数据) 。如果读取进程没有关闭管道的写入端,那么在其他进程关闭其写入描述符后,读取器将不会看到文件结尾,即使它已从管道读取了所有数据。相反,aread()会阻塞等待数据,因为内核知道管道中至少还有一个打开的写描述符。该描述符由读进程本身保持打开状态是无关紧要的;理论上,该进程仍然可以写入管道,即使它在尝试读取时被阻止。例如,read()可能会被将数据写入管道的信号处理程序中断。写入进程出于不同的原因关闭其管道的读取描述符。当进程尝试写入没有进程打开的读描述符的管道时,内核会SIGPIPE向写入进程发送信号。默认情况下,此信号会终止进程。相反,进程可以安排捕获或忽略此信号,在这种情况下,write()管道上的操作会失败并出现错误EPIPE(管道损坏)。接收SIGPIPE信号或获取EPIPE错误是有关管道状态的有用指示,这就是为什么应关闭管道的未使用的读取描述符的原因。如果写入进程没有关闭管道的读取端,那么即使其他进程关闭了管道的读取端,写入进程也会填充管道,并且进一步的写入尝试将无限期地阻塞。关闭未使用的文件描述符的最后一个原因是,只有在所有文件描述符都关闭后,管道才会被销毁,并且其资源会被释放以供其他进程重用。此时,管道中所有未读的数据都会丢失。
~ Micheal Kerrisk,Linux 编程接口