【发布时间】:2013-10-16 09:48:02
【问题描述】:
当使用管道进行进程间通信时,关闭管道一端的目的是什么?
例如:How to send a simple string between two programs using pipes?
请注意,管道的一侧在子进程和父进程中是关闭的。为什么需要这个?
【问题讨论】:
当使用管道进行进程间通信时,关闭管道一端的目的是什么?
例如:How to send a simple string between two programs using pipes?
请注意,管道的一侧在子进程和父进程中是关闭的。为什么需要这个?
【问题讨论】:
如果您使用管道连接两个进程(父进程和子进程),则在分叉之前创建管道。
fork 使两个进程都可以访问管道的两端。这是不可取的。
如果读取端注意到 EOF 条件,它应该知道写入器已完成。这只有在所有写入面都关闭时才会发生。所以最好尽快关闭写FD。
编写器应关闭其读取 FD,以免打开太多 FD,从而达到可能存在的打开 FD 限制。此外,如果当时唯一的读取器死了,写入器会通过收到一个 SIGPIPE 或至少一个 EPIPE 错误(取决于信号的定义方式)得到通知。如果有多个阅读器,作者无法检测到“真正的阅读器”消失了,继续写入并卡住,因为正在写入的 FD 阻塞,希望“未使用”的阅读器会读到一些东西。
所以这里详细说明会发生什么:
pipe()并获得2个文件描述符:我们称之为rd和wr。fork()。现在两个进程都有一个rd 和一个wr。假设子进程应该是阅读器。
然后
【讨论】:
在给定时间可以打开的文件描述符的数量是有限的。如果您一直打开管道而不是很快关闭它们,您将用完 FD 并且无法再打开任何东西:不是管道,不是文件,不是套接字,...
关闭管道很重要的另一个原因是关闭本身对应用程序有意义。例如,管道的一个常见用法是在使用fork 和exec 到launch an external program 时将errno 从子进程发送到父进程:
fork创建子进程,关闭其写入端,并尝试从管道读取。 exec 运行不同的程序:
exec失败,例如因为程序不存在,子进程将errno写入管道,父进程读取它就知道出了什么问题,并且可以告诉用户。 exec 成功,则管道关闭,没有写入任何内容。父级中的read 函数返回0,表示管道已关闭,并且知道程序已成功启动。如果父级在尝试从管道读取之前没有关闭管道的写入端,这将不起作用,因为当exec 成功时,read 函数将永远不会返回。
【讨论】:
关闭未使用的管道文件描述符不仅仅是确保进程不会耗尽其有限的文件描述符集 - 这对于正确使用管道至关重要。我们现在考虑为什么必须关闭管道读写端未使用的文件描述符。
从管道读取的进程会关闭其对管道的写入描述符,因此,当另一个进程完成其输出并关闭其写入描述符时,读取会看到文件结尾(一旦它准备好管道中的任何未完成数据) .
如果读取进程没有关闭管道的写入端,那么在另一个进程关闭其写入描述符后,即使读取器从管道读取了所有数据,读取器也不会看到文件结尾。相反,read() 会阻塞等待数据,因为内核知道至少还有一个为管道打开的写描述符。这个描述符被读进程本身保持打开是无关紧要的;理论上,该进程仍然可以写入管道,即使它在尝试读取时被阻止。
例如,read() 可能会被将数据写入管道的信号处理程序中断。
写入过程出于不同的原因关闭其对管道的读取描述符。
当一个进程试图写入一个没有进程打开读取描述符的管道时,内核会向写入进程发送SIGPIPE 信号。默认情况下,此信号会终止一个进程。相反,进程可以安排捕获或忽略此信号,在这种情况下,管道上的 write() 失败并出现错误 EPIPE(管道损坏)。接收SIGPIPE 信号或获得EPIPE 错误是关于管道状态的有用指示,这就是为什么应该关闭管道的未使用读取描述符的原因。
如果写入进程没有关闭管道的读取端,那么即使在其他进程关闭管道的读取端之后,写入进程也会填满管道,进一步的写入尝试将无限期阻塞。
关闭未使用的文件描述符的最后一个原因是,只有在所有文件描述符都关闭之后,管道才会被销毁并释放其资源以供其他进程重用。此时,管道中所有未读数据都会丢失。
~ Micheal Kerrisk,Linux 编程接口
【讨论】: