read() 不会阻塞在没有 O_NONBLOCK 标志的情况下打开的空 FIFO

read() doesn't block on empty FIFOs opened without O_NONBLOCK flag

pipe(7) 说:

If a process attempts to read from an empty pipe, then read(2) will block until data is available. If a process attempts to write to a full pipe (see below), then write(2) blocks until sufficient data has been read from the pipe to allow the write to complete. Nonblocking I/O is possible by using the fcntl(2) F_SETFL operation to enable the O_NONBLOCK open file status flag.

下面我用 gcc 在 linux 上编译了两个简单的 C 程序:

reader.c:

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

#define STACKBUF_SIZE 128
#define FIFO_PATH "/home/bogdan/.imagedata"

signed int main(int argc, char **argv) {
    int fifo_fd = open(FIFO_PATH, O_RDONLY); // blocking... - notice no O_NONBLOCK flag
    if (fifo_fd != -1) {
        fprintf(stdout, "open() call succeeded\n");
    }

    while (1) {
        char buf[STACKBUF_SIZE] = {0};
        ssize_t bread = read(fifo_fd, buf, STACKBUF_SIZE);
        fprintf(stdout, "%d - %s\n", bread, buf);
        sleep(1);
    }

    close(fifo_fd);

    return EXIT_SUCCESS;
}

writer.c:

#define STACKBUF_SIZE 128
#define FIFO_PATH "/home/bogdan/.imagedata"
#define DATA "data"

int main(void) {
    int fifo_fd = open(FIFO_PATH, O_WRONLY); // blocks until reader opens on the reader end, however we always first open the reader so...
    if(fifo_fd != -1) {
        ssize_t bwritten = write(fifo_fd, DATA, 5);
        fprintf(stdout, "writer wrote %ld bytes\n", bwritten);
    }
    
    close(fifo_fd);
    return EXIT_SUCCESS;
}

这些文件被编译成两个单独的二进制文件 gcc writer.c -Og -g -o ./writer,reader 也是如此。

从 shell 我首先执行 reader 二进制文件,并且正如预期的那样,初始 open() 调用阻塞,直到我也执行编写器。然后我执行编写器,它的 open() 调用立即成功并将 5 个字节写入 FIFO(由 reader 正确显示),之后关闭 fd, 离开FIFO 为空 (?).

但是,reader 的 while 循环中的以下 read() 调用根本不会阻塞,而只是 return 0.

除非我遗漏了什么(我可能遗漏了什么)这与 pipe(7) 联机帮助页概述的语义冲突,因为 FIFO fd 在 reader 和作者。

您引用的手册部分仅适用于打开编写器的管道。往下两段,它是这样说的:

If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0).