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

标签 linux ipc pipe

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

正常情况下,当发送进程关闭管道的写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 正在阅读的管道,这种情况仍在发生。

查看源代码会发现问题。当我 fork 第二个子进程时,它获取了自己的 pipe1 文件描述符副本,这些文件描述符保持打开状态。尽管只有一个进程应该写入管道,但在第二个进程中打开它可以防止它进入 EOF 状态。

问题没有出现在小数据集上,因为 child2 很快就完成了它的业务,并退出了。但是对于更大的数据集,child2 并没有很快返回,我最终陷入了僵局。

最佳答案

当写者关闭写端时,read 应该返回 EOF。

因为你做了一个管道然后一个 fork ,两个进程都会打开写 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 */
  } 
}

关于linux - 使用 EOF 在未命名管道上发送信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5425938/

相关文章:

linux - 为什么管道上的这个 strace 没有完成

c - 为多条消息重用一个管道

作为 jenkins 从机的 Linux/mac 权限

python - Celery 任务执行来自另一种编程语言的代码

c++ - 单例同步 C++

javascript - 浏览器的快速IPC方法

反引号命令中的 bash pipestatus?

linux - 如何将 child 添加到 erlang supervisor?

linux - 创建重复文件并重命名

c - 如何在 ELF 中分配可写/可读段?