使用EOF在未命名的管道上发送信号

时间:2011-03-24 21:43:09

标签: linux ipc pipe

我有一个测试程序,它使用用pipe()创建的未命名管道在Linux系统上用fork()创建的父进程和子进程之间进行通信。

通常,当发送进程关闭管道的写入fd时,接收进程从read()返回值0,表示EOF。

然而,似乎如果我填充了相当大量数据的管道(在接收器开始读取之前可能是100K字节0),接收器在读取管道中的所有数据后会阻塞 - 即使发送方已关闭它。

我已经验证发送过程已经用lsof关闭了管道,并且看起来非常清楚接收器被阻塞了。

这导致了一个问题:关闭管道的一端是一种可靠的方法让接收者知道没有更多的数据吗?

如果是,并且没有条件可以导致空的,关闭的FIFO上的read()阻塞,那么我的代码就会出错。如果没有,这意味着我需要找到一种发信号通知数据流结束的替代方法。

解决

我很确定最初的假设是正确的,关闭管道导致阅读器侧的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没有快速返回,我最终陷入僵局。

1 个答案:

答案 0 :(得分:6)

当写入器关闭写入结束时,读取应返回EOF。

由于你做了一个管道,然后是一个fork,两个进程都会打开write fd。可能是在读者进程中你忘记关闭管道的写入部分。

警告:自从我在Unix上编程以来已经有很长一段时间了。所以它可能不准确。

以下是一些代码:http://www.cs.uml.edu/~fredm/courses/91.308/files/pipes.html。请查看下面的“未使用的”评论。

#include <stdio.h>

/* The index of the "read" end of the pipe */
#define READ 0

/* The index of the "write" end of the pipe */
#define WRITE 1

char *phrase = "Stuff this in your pipe and smoke it";

main () {
  int fd[2], bytesRead;

  char message [100]; /* Parent process message buffer */

  pipe ( fd ); /*Create an unnamed pipe*/

  if ( fork ( ) == 0 ) {
    /* Child Writer */
    close (fd[READ]); /* Close unused end*/
    write (fd[WRITE], phrase, strlen ( phrase) +1); /* include NULL*/
    close (fd[WRITE]); /* Close used end*/
    printf("Child:  Wrote '%s' to pipe!\n", phrase);

  } else {

    /* Parent Reader */
    close (fd[WRITE]); /* Close unused end*/ 
    bytesRead = read ( fd[READ], message, 100);
    printf ( "Parent: Read %d bytes from pipe: %s\n", bytesRead, message);
    close ( fd[READ]); /* Close used end */
  } 
}