Linux fifo 缓冲采坑
本文记录了我在使用 fifo(命名管道)的过程中由于缓冲踩的坑。
事情的经过是这样的:我有一个 python 脚本,它会实时向标准输出打印数据,现在我想把这些打印到的标准输出数据导入 FIFO 中,并且在另一个 Qt 程序中从 FIFO 读取数据,于是我开始了下述操作。
首先用 mkfifo tmp.fifo
创建了一个 FIFO 文件,然后以下述方式运行我的 python 脚本
python tmp.py > tmp.fifo
另一边创建了一个 Qt 程序打开这个 FIFO 读取数据
QFile fifo(DEMO_FIFO);
if (!fifo.open(QIODevice::ReadOnly | QIODevice::Text )) {
qDebug() << "Open fifo: " << DEMO_FIFO << " error" << endl;
return -1;
}
qDebug() << "Open fifo: " << DEMO_FIFO << " success" << endl;
char buf[1024];
while (true) {
qint64 size = fifo.readLine(buf, sizeof(buf));
if (size >= 0){
qDebug() << buf;
}
}
两个程序都运行后,出现了一种比较诡异的现象:python 脚本向 FIFO 写入了大量数据后,Qt 程序才会从 readLine()
函数返回,正常情况下应该是 FIFO 写入一行数据,Qt 程序就能够从 FIFO 读出一行数据,但实际情况不是这样,当 FIFO 被写入了大量数据后,才允许从 FIFO 另一端读取数据。
我琢磨了一下午才想到是缓冲区的问题。
C 标准库 IO 函数分为三种缓冲类型:
- 全缓冲:缓冲区填满,或使用
fflush()
将缓冲区内容刷洗到 IO 设备。 - 行缓冲:缓冲区填满,或写入
\n
,或使用fflush()
将缓冲区内容刷洗到 IO 设备。 - 无缓冲:写入字节立即刷洗到 IO 设备。
那么 FIFO 是属于哪一种缓冲区呢?我在 APUE 里找到了下面这几句话:
- 标准错误是不带缓冲的。
- 若是指向终端设备的流,则是行缓冲的,否则是全缓冲的。
因此,FIFO 是全缓冲设备,这也解释了为什么上面介绍的 python 脚本将数据打印到标准输出时数据是一行一行的显示,而打印到 FIFO 时,数据只能是一大块一大块的读取。
找到了问题的缘由,我更改了 python 脚本的 print()
函数:
print(f"{json.dumps(data)}"); --> print(f"{json.dumps(data)}", flush=True);
每写入一行数据,自动刷洗数据,这样就成功解决了问题。