【发布时间】:2011-07-22 12:11:32
【问题描述】:
我有一个测试程序,它使用使用 pipe() 创建的未命名管道在 Linux 系统上使用 fork() 创建的父进程和子进程之间进行通信。
一般情况下,当发送进程关闭管道的write fd时,接收进程从read()返回值为0,表示EOF。
但是,如果我在管道中填充相当大量的数据(在接收器开始读取之前可能是 100K 字节0),则接收器在读取管道中的所有数据后会阻塞 - 即使发送器已关闭它。
我已经验证了发送进程已经用lsof关闭了管道,而且接收方被阻塞的情况似乎很明显。
这导致了一个问题:关闭管道的一端是让接收者知道没有更多数据的可靠方法吗?
如果是这样,并且没有任何条件可以导致 read() 阻塞空的、关闭的 FIFO,那么我的代码就有问题。如果没有,这意味着我需要找到一种替代方法来发出数据流结束的信号。
分辨率
我很确定最初的假设是正确的,关闭管道会导致阅读器端出现 EOF,这个问题只是在黑暗中的一个镜头——我想也许我忽略了一些微妙的管道行为。几乎你见过的每个管道示例都是发送几个字节并退出的玩具。当您不再执行原子操作时,事情通常会有所不同。
无论如何,我尝试简化代码以解决问题,并成功找到了我的问题。在伪代码中,我最终做了这样的事情:
create pipe1
if ( !fork() ) {
close pipe1 write fd
do some stuff reading pipe1 until EOF
}
create pipe2
if ( !fork() ) {
close pipe2 write fd
do some stuff reading pipe2 until EOF
}
close pipe1 read fd
close pipe2 read fd
write data to pipe1
get completion response from child 1
close pipe1 write fd
write data to pipe2
get completion response from child 2
close pipe2 write fd
wait for children to exit
读取 pipe1 的子进程挂起,但仅当管道中的数据量变得很大时。即使我关闭了 child1 正在读取的管道,也会发生这种情况。
查看源代码会发现问题。当我派生第二个子进程时,它获取了自己的 pipe1 文件描述符副本,这些文件描述符保持打开状态。即使只有一个进程应该写入管道,但在第二个进程中打开它可以防止它进入 EOF 状态。
小数据集并没有出现问题,因为 child2 正在快速完成其业务并退出。但是对于更大的数据集,child2 并没有快速返回,我最终陷入了僵局。
【问题讨论】: