You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

写入未读取的FIFO:字节去向与内核读写管理机制问询

FIFO管道的内核读写机制疑问与实验验证

初始实验与核心疑问

初始代码

Reader程序:

// reader
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    int fd = open("fifo.txt", O_RDONLY);
    sleep(15);
    close(fd);
    return 0;
}

Writer程序:

// writer
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    int fd = open("fifo.txt", O_WRONLY);
    write(fd, "hi", 3);
    close(fd);
    return 0;
}

实验操作与现象

  1. 先通过mkfifo fifo.txt创建命名管道
  2. 执行./reader & ./writer &并行运行两个程序
  3. 待写入者结束、读者仍处于休眠状态时,用cat fifo.txt尝试读取管道,无数据返回;读者结束后再次尝试,依然无法读取写入的3字节
  4. 只有在读者关闭管道前执行读操作,才能获取到这3字节

核心疑问

  • 若无人读取但管道文件仍存在,写入的3字节数据去向何方?
  • 内核如何管理FIFO的读写权限与数据流转(比如多进程非阻塞打开、读写后退出的场景)?

补充实验与现象分析

修改后的实验代码

Reader程序:

// reader
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    char buf[3] = {'\0'};

    int fd = open("fifo.txt", O_RDONLY);
    sleep(15);
    ssize_t bytes = read(fd, buf, sizeof(buf));
    printf("[Reader] Read %s (%d chars)\n", buf, (int)bytes); fflush(stdout);
    close(fd);
    return 0;
}

Writer程序:

// writer
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd = open("fifo.txt", O_WRONLY);
    sleep(2);
    write(fd, "hi", 3);
    write(fd, "ha", 3);
    close(fd);
    return 0;
}

实验现象

  1. 单个写入者+单个读者:读者最多读取3字节(受缓冲区大小限制)
  2. 单个写入者+两个读者:一个读者读取到"hi",另一个读取到"ha"
  3. 若将读者缓冲区增大至10字节:一个读者读取到hi\0ha\0,另一个读取0字节

基于现象猜测:内核可能维护写入发生时已打开FIFO的进程队列,仅这些进程可读取数据,但需要明确内核管理的具体细节。

内核对FIFO的管理细节

1. 未被读取的数据去向

FIFO是内核维护的临时字节流缓冲区,不具备持久化存储能力:

  • 当写端写入数据时,只有存在已打开的读端进程,数据才会被存入内核缓冲区;
  • 若所有读端进程关闭,即使写端曾写入数据,内核会直接丢弃缓冲区中未被读取的数据——因为FIFO的设计逻辑是“为正在通信的读写进程提供中转”,没有读端存在时,数据没有接收方,自然不会保留;
  • 实验中,写入者结束后读者仍休眠,此时唯一的读端还打开,但cat fifo.txt是新打开的读端,此时缓冲区的数据仅对原读端(休眠的reader进程)可见;当原读端未读取就关闭时,数据会被内核直接丢弃,后续新打开的读端只能读到EOF。

2. FIFO的读写权限与进程管理逻辑

内核通过读写端引用计数和环形缓冲区管理FIFO:

  • 打开规则:
    • 阻塞模式(默认):O_RDONLY打开会阻塞到至少一个写端打开;O_WRONLY打开会阻塞到至少一个读端打开;O_RDWR模式直接打开,不会阻塞(因为自身同时具备读写端)。
    • 非阻塞模式(O_NONBLOCK):O_RDONLY直接打开(即使无写端);O_WRONLY如果当前无读端,会直接返回ENXIO错误。
  • 数据流转规则:
    • FIFO是字节流模式,所有打开的读端共享同一个内核缓冲区,数据被任意一个读端读取后,就会从缓冲区中移除,不会被其他读端重复读取;
    • 当有多个读端时,数据会按“先到先得”的流式方式分配,比如实验中两个小缓冲区读端会各分到一段数据,而大缓冲区读端会一次性读走所有数据,导致另一个读端无数据可读;
    • 不存在“写入时已打开的进程队列”,只要读端处于打开状态,就可以读取缓冲区中的数据,直到数据被读完或写端关闭触发EOF。
  • 资源回收:
    • 当所有读端关闭,写端的后续写操作会触发SIGPIPE信号(默认导致进程退出),同时返回EPIPE错误;
    • 当所有写端关闭,读端的后续读操作会返回0(表示EOF);
    • 当所有读写端都关闭,内核会清理FIFO的缓冲区资源,但FIFO的文件节点(在文件系统中)会保留,直到被unlink删除。

内容的提问来源于stack exchange,提问作者ABu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 07:15:56