POSIX 命名管道 (fifo) 以非阻塞模式删除记录

POSIX named pipe (fifo) drops record in nonblocking mode

我正在使用 POSIX 命名管道 (fifos) 从一个或多个线程发送记录以供另一个线程读取(只有一个线程进行读取)。然而,100 条记录中的第 83 条记录被简单地删除了。客户端核心调用 write 并且 return 值被正确报告为记录的长度(720 字节),因此客户端(编写器)核心确认记录已发送,但切换到 reader 核心在启用调度程序锁定的 gdb 调试模式下,我循环读取之前的几条记录,然后读取失败——管道中没有记录,即使客户端(编写器)核心确认了写入。

管道容量为 65,536 字节(Linux 中默认)。我假设每读取一条记录,管道内容就会减少 1 条记录,因此在删除第 83 条记录时,管道中有大约 5 条先前的记录,即 3600 字节——不足以填满管道。

我以非阻塞模式打开管道,因为当我以阻塞模式打开它们时,两端都冻结了。根据 http://man7.org/linux/man-pages/man7/fifo.7.html、"The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also." 的手册页,我的问题是两端都阻塞并且不会更进一步。它还说,"Under Linux, opening a FIFO for read and write will succeed both in blocking and nonblocking mode. POSIX leaves this behavior undefined."

两端的代码很简单:

int64_t fifo_write(int fd, const void *buf, size_t count) {

    int status_write = write(fd, buf, count);

    return status_write; }

int64_t fifo_read(int fd, void *buf, size_t count) {

    int status_read = read(fd, buf, count); 

    return status_read; }

C 函数是从我的 NASM 程序中调用的:

mov rdi,[fifo_read_fd]
lea rsi,[fifo_buffer]
mov rdx,720
call fifo_read wrt ..plt

mov rdi,[fifo_write_fd]
mov rsi,[rbp-24]
mov rdx,720 ; bytes
push r11
push rcx
call fifo_write wrt ..plt
pop rcx
pop r11

我的问题是:

  1. 什么会导致记录丢失?它看起来不像管道容量,除非管道没有随着每条记录的读取而清空——即使所有 83 条记录也将占用 59760 字节,低于 Linux 中的 65K 管道容量。这可能是由于非阻塞模式,但如果管道未满,则没有理由阻塞。

  2. 如何在阻塞模式下打开两端(假设两端冻结,各自等待对方),阻塞模式会不会有任何问题?

  3. 我可以在 read/write 模式下打开两端,因为我的代码仅在一端从一个或多个线程写入,而在另一端(仅)从 1 个线程读取。而 "POSIX leaves this behavior undefined" 有什么理由不在这种情况下以 read/write 模式打开两端?

我没有针对此问题发布任何其他代码(上述除外),因为我只是在寻找处理我描述的情况下记录丢失问题的最佳方法的想法。

您有多个写入器使用一个 FIFO 发送 720 字节的消息。 POSIX 只要求 PIPE_BUF(通常为 512 字节)的写入是原子的。这意味着较长的写入可能会被其他线程的写入交错并损坏。

无论 PIPE_BUF 大小如何,管道都是流,它们没有消息的概念,这意味着您需要自己分隔消息,而您的代码不会这样做。换句话说,当有多个作者时,您的 reader 代码不可能恢复单独的消息。

您可能想改用 Unix datagram socket。进入 Unix 数据报套接字的每条消息都是一条原子消息,它在一个系统调用中完全写入和读取(sendtorecvfrom)。