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

非阻塞读取FIFO时偶现数据丢失问题排查

问题分析与解决方案

你的reader程序偶尔无法读取到echo写入FIFO的内容,主要有以下几个原因:

1. FIFO文件名不匹配

你的程序打开的是当前目录下的the_fifo,但执行写入时用的是echo "[string]" > fifo_in——如果fifo_in和the_fifo不是同一个管道(比如名字拼写错误、不在同一目录),自然读不到数据。如果是偶尔写错文件名,就会出现“偶尔收不到”的现象。

2. 未处理read的错误返回值与空数据情况

你的代码忽略了read返回-1或0的情况,加上无间隔的死循环,会导致两个问题:

  • 当O_NONBLOCK模式下无数据可读时,read返回-1且errno=EAGAIN,此时代码会输出空字符串,同时持续占用100%CPU,可能影响系统调度,导致无法及时响应新的写入数据。
  • 当写进程关闭FIFO后,read返回0(EOF),代码同样输出空字符串,但后续新写进程的数据仍能被读取,不过CPU占用问题会加剧调度延迟。

3. 错误的输出逻辑

每次循环强制输出line并清空,即使没有读到任何数据,这会产生无效的空输出,同时如果因为调度延迟导致数据未被及时读取,也会出现“没收到”的错觉。


修复后的代码方案

方案一:使用阻塞模式(推荐,更可靠)

阻塞模式下,read会自动等待数据,无需循环轮询,CPU占用极低:

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    int fd = open("the_fifo", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    char buf[32];
    std::string line = "";
    while(1) {
        ssize_t n_read = read(fd, buf, sizeof(buf));
        if(n_read > 0) {
            line.append(buf, n_read);
            std::cout << line;
            std::cout.flush();
            line = "";
        } else if (n_read == 0) {
            // 写端关闭,下一次read会自动阻塞等待新的写进程
            continue;
        } else {
            perror("read failed");
            close(fd);
            return 1;
        }
    }
    close(fd);
    return 0;
}

方案二:保留非阻塞模式但修复逻辑

如果必须使用非阻塞模式,需要添加错误处理和休眠逻辑,避免CPU占用过高:

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    int fd = open("the_fifo", O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    char buf[32];
    std::string line = "";
    while(1) {
        ssize_t n_read = read(fd, buf, sizeof(buf));
        if(n_read > 0) {
            line.append(buf, n_read);
            std::cout << line;
            std::cout.flush();
            line = "";
        } else if (n_read == 0) {
            // 写端关闭,无需操作
            continue;
        } else {
            if (errno != EAGAIN) {
                perror("read failed");
                close(fd);
                return 1;
            }
            // 无数据时休眠,降低CPU占用
            usleep(1000);
        }
    }
    close(fd);
    return 0;
}

额外注意事项

  • 确保写入命令的FIFO名称与程序打开的一致:执行echo "[string]" > the_fifo(而非fifo_in)。
  • 运行程序前,务必在当前目录执行mkfifo the_fifo创建管道,且确保程序有读取权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 06:27:32