【问题标题】:Why are my writes not blocking to this pipe?为什么我的写入没有阻塞到这个管道?
【发布时间】:2018-06-06 21:56:57
【问题描述】:

我正在编写一个小 Linux fifo 测试程序。

我使用mkfifo mpipe 创建了一个管道。程序应该对发送给它的每个参数执行一次写入。如果未发送任何参数,则从管道执行一次读取。

这是我的代码

int main(int argc, char *argv[])
{
    if (argc > 1)
    {
       int fd = open("./mpipe", O_WRONLY);
       int i = 1;
       for (i; i < argc; i++)
       {
           int bytes = write(fd, argv[i], strlen(argv[i]) + 1);
           if (bytes <= 0)
           {
               printf("ERROR: write\n");
               close(fd);
               return 1;
           }
           printf("Wrote %d bytes: %s\n", bytes, argv[i]);
       }

       close(fd);
       return 0;
    }

    /* Else perform one read */
    char buf[64];
    int bytes = 0;

    int fd = open("./mpipe", O_RDONLY);
    bytes = read(fd, buf, 64);
    if (bytes <= 0)
    {
        printf("ERROR: read\n");
        close(fd);
        return 1;
    }
    else
    {
        printf("Read %d bytes: %s\n", bytes, buf);
    }

    close(fd);
    return 0;
}

我希望行为是这样的......

我打电话给./pt hello i am the deepest guy,我希望它会阻止 6 次读取。相反,一次读取似乎足以触发多次写入。我的输出看起来像

# Term 1 - writer
$: ./pt hello i am the deepest guy # this call blocks until a read, but then cascades.
Just wrote hello
Just wrote i
Just wrote am
Just wrote the # output ends here

# Term 2 - reader
$: ./pt
Read 6 bytes: hello

有人可以帮助解释这种奇怪的行为吗?我认为就管道通信而言,每次读取都必须与写入匹配。

【问题讨论】:

  • 一次读取可以从多次写入中读取数据,正如您所展示的。写入的原子性意味着来自不同进程的写入不会被交错; P1 的所有消息都将在 P2 的所有消息之前,反之亦然,受大小​​限制。
  • 把你的read()放在一个循环中。顺便说一句:您发送“你好”加上一个 NUL 字符(总大小 = 6)
  • 写入进程在读取之前不会阻塞,它会一直阻塞直到打开。即作者中的open 被阻塞,直到读者调用open。然后作者正在填充缓冲区。围绕未决调用添加一些诊断。
  • 哪个 open 是第一个阻塞直到另一个(默认情况下),但是一旦两端都打开,数据就会被缓冲,并且写入器可以领先于读取器一定量这可能取决于系统; for (modern) Linux it defaults to 64kiB.
  • 这就解释了为什么有时我的输出是不确定的;这只是一个竞争条件。因此,如果我修改了我的数据以预先设置传入消息的大小,那么程序可以注意这一点。

标签: c linux pipe fifo


【解决方案1】:

那里发生的情况是内核在open(2) 系统调用中阻塞了写入进程,直到您有读取器打开它进行读取。 (fifo 要求两端都连接到进程才能工作)一旦读取器执行第一个read(2) 调用(写入器或读取器块,谁首先获得系统调用)内核将所有数据从写入器传递到阅读器,并唤醒两个进程(这就是只接收第一个命令行参数的原因,而不是来自写入器的前 16 个字节,您只能从阻塞写入器获得六个字符 {'h', 'e', 'l', 'l', 'o', '\0' }

最后,当阅读器关闭先进先出时,写入者被SIGPIPE 信号杀死,因为没有更多阅读器打开先进先出。如果您在写入程序进程上安装信号处理程序(或忽略信号),您将收到来自write(2) 系统调用的错误,告诉您在阻塞时没有更多的读取器在 fifo(EPIPEerrno 值)上被阻塞写。

请注意,这是一项功能,而不是错误,是一种知道在您关闭并重新打开 fifo 之前写入不会到达任何阅读器的方法。

内核会为整个read(2)write(2) 调用阻塞fifo 的inode,因此即使另一个进程在fifo 上执行另一个write(2) 也会被阻塞,并且您将无法获得阅读器上的数据来自第二位作家(如果你有的话)。如果您愿意,可以尝试启动两个作家,看看会发生什么。

$ pru I am the best &
[1] 271
$ pru
Read 2 bytes: I
Wrote 2 bytes: I
Wrote 3 bytes: am
[1]+  Broken pipe             pru I am the best  <<< this is the kill to the writer process, announced by the shell.
$ _

【讨论】:

  • 谢谢,现在我对管道有了更深入的了解。
猜你喜欢
  • 1970-01-01
  • 2016-09-23
  • 1970-01-01
  • 1970-01-01
  • 2017-07-14
  • 2016-07-10
  • 1970-01-01
  • 1970-01-01
  • 2014-08-11
相关资源
最近更新 更多