为啥在没有人阅读后继续写入命名管道?

Posted

技术标签:

【中文标题】为啥在没有人阅读后继续写入命名管道?【英文标题】:Why does writing to a named pipe continue after no one is reading?为什么在没有人阅读后继续写入命名管道? 【发布时间】:2016-01-05 21:43:18 【问题描述】:

我正在做一些实验来了解命名管道。据我了解,操作系统将阻止写入命名管道的程序,直到另一个程序从命名管道读取。所以我写了两个程序,startloopreadbytestartloop 创建一个 fifo 并在每次读取客户端 (readbyte) 时不断写入它:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

int main(int argc, char *argv[]) 
    const char num = 123;
    mkfifo("fifo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
    int fd = open("fifo", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    while (1) 
        printf("loop_start\n");
        write(fd, &num, sizeof(num));
    
    close(fd);
    return 0;

readbyte 在运行时从 fifo 读取一个字节:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main(int argc, char *argv[]) 
    char num;
    int fd;
    if ((fd = open(argv[1], O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) 
        perror("Cannot open input file\n"); exit(1);
    

    read(fd, &num, sizeof(num));
    printf("%d\n", num);
    close(fd);
    return 0;

readbyte 在“fifo”上运行时按预期打印数字:

hostname:dir username$ ./readbyte fifo 
65

正如我所料,loopstart 在我使用readbyte 从 fifo 读取之前不会打印任何内容。但是,当它变得畅通时,它会多次写入“fifo”,而不是立即被挂起。这是为什么呢?

hostname:dir username$ ./startloop
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start
loop_start

【问题讨论】:

你怎么知道write()调用成功了?你没有检查它的返回值。 @Andrew Henle-我知道它至少以某种方式成功,因为readbyte 打印了所写的内容。我确实有一个带有错误检查的版本——不过,我删除了所有这些以使帖子更短。如果您认为这是问题,我可以重新添加。 你的循环继续的事实仅仅意味着write()没有阻塞,而不是成功。有很大的不同。如果您不知道发生了什么并需要帮助,您发布的数据越多,您获得的帮助就越好。在这种情况下,发出循环计数器以及来自write() 的返回值将比简单的“loop_start”好得多。这是一个很容易解决的问题 - 解决更难的问题将需要额外的信息。 我同意添加这些信息是个好主意。在这种情况下,它实际上可以隐藏问题。当我添加一个循环计数器时,我得到了一行输出,loop 0, wrote 1 bytes。有时当我跑步时,我会得到两条线。 printf 使用额外参数所花费的时间最终隐藏了执行的循环数。 【参考方案1】:

“据我了解,操作系统将阻止写入命名管道的程序,直到另一个程序从命名管道读取。”

这种理解是错误的。 write 不会阻塞,除非管道/fifo 已满。来自pipe manul:

管道的容量有限。如果管道已满,则 write(2) 将阻塞或失败,取决于是否设置了 O_NONBLOCK 标志 (见下文)。

至于为什么第一个 write 似乎会阻止 - 实际上并没有。阻止的是open。来自fifo manaul:

FIFO 必须在两端(读取和写入)都打开,然后才能数据 可以通过。通常,打开 FIFO 块直到另一端 也打开了。

更新:实际上上述情况对于第一个write 是正确的。但可能还有更多的解释。一旦readbyte 程序关闭先进先出,随后的write 调用应该开始失败。

【讨论】:

一旦阅读器关闭 fifo,写入调用将失败并显示 EPIPE,但前提是 SIGPIPE 被忽略。由于默认情况下不会忽略该信号,并且默认操作是终止进程,因此您可以预期多次写入成功,直到管道关闭,然后进程死亡。 @rici 谢谢并同意,这是一个有价值的澄清。【参考方案2】:

测试写入结果

 while (1) 
        printf("loop_start\n");
     int ret =  write(fd, &num, sizeof(num));
     if(ret == -1)
     
        perror("error writing to fifo");
        exit(1);
     
    

【讨论】:

以上是关于为啥在没有人阅读后继续写入命名管道?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 bash 在写入命名管道时关闭?

检测阅读器何时关闭命名管道(FIFO)

如何写入命名管道而不等待读取管道

当阅读器断开连接时,命名管道 (FIFO) 数据会去哪里?

在 bash 中打开命名管道,而不读取或写入它

如何避免命名管道中的多个作者?