写入 Linux 管道的速度比文件快,但在内核级别,为啥?
Posted
技术标签:
【中文标题】写入 Linux 管道的速度比文件快,但在内核级别,为啥?【英文标题】:Writing to Linux pipe faster than file, but on a kernel level, why?写入 Linux 管道的速度比文件快,但在内核级别,为什么? 【发布时间】:2020-12-31 12:35:00 【问题描述】:我正在研究写入文件与管道的速度。请看这段代码,除非有命令行参数,否则写入文件句柄,否则写入管道:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <chrono>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
void do_write(int fd)
const char* data = "Hello world!";
int to_write = strlen(data), total_written = 0;
int x = 0;
auto start = chrono::high_resolution_clock::now();
while (x < 50000)
int written = 0;
while (written != to_write)
written += write(fd, data + written, to_write - written);
total_written += written;
++x;
auto end = chrono::high_resolution_clock::now();
auto diff = end - start;
cout << "Total bytes written: " << total_written << " in " << chrono::duration<double, milli>(diff).count()
<< " milliseconds, " << endl;
int main(int argc, char *argv[])
//
// Write to file if we have not specified any extra argument
//
if (argc == 1)
int fd = open("test.txt", O_WRONLY | O_TRUNC | O_CREAT, 0655);
if (fd == -1) return -1;
do_write(fd);
return 0;
//
// Otherwise, write to pipe
//
int the_pipe[2];
if (pipe(the_pipe) == -1) return -1;
pid_t child = fork();
switch (child)
case -1:
return -1;
case 0:
char buf[128];
int bytes_read = 0, total_read = 0;
close(the_pipe[1]);
while (true)
if ((bytes_read = read(the_pipe[0], buf, 128)) == 0)
break;
total_read += bytes_read;
cout << "Child: Total bytes read: " << total_read << endl;
break;
default:
close(the_pipe[0]);
do_write(the_pipe[1]);
break;
return 0;
这是我的输出:
$ time ./LinuxFlushTest pipe
Total bytes written: 600000 in 59.6544 milliseconds,
real 0m0.064s
user 0m0.020s
sys 0m0.040s
Child: Total bytes read: 600000
$ time ./LinuxFlushTest
Total bytes written: 600000 in 154.367 milliseconds,
real 0m0.159s
user 0m0.028s
sys 0m0.132s
从time
输出和我的 C++ 代码时序中,您可以看到写入管道比文件快得多。
现在,据我所知,当我们调用write()
时,数据将被复制到内核缓冲区,此时pdflush
样式线程实际上会将其从页面缓存刷新到底层文件。 我没有在我的代码中强制刷新,因此没有磁盘查找延迟。
但是我不知道(而且似乎无法找到:是的,我看过内核代码但迷失在其中,所以没有像“看代码”这样的 cmets请) 写入管道时会发生什么不同:它不只是内核中某个孩子可以读取的内存块吗?那么,为什么它比写入文件的基本相同的过程要快得多?
【问题讨论】:
【参考方案1】:现在,据我所知,当我们调用
write()
时,数据将被复制 到内核缓冲区,此时pdflush
样式线程将 实际上将它从页面缓存刷新到底层文件。 我是 没有在我的代码中强制刷新,因此没有磁盘查找延迟。
您似乎对此有一些误解,包括:
您不必显式地为内核执行任何操作来将写入的数据刷新到底层输出设备。它可能会自行决定将内存中的部分甚至全部数据缓冲一段时间,但可以预期的是,即使没有来自用户空间的明确指令,内核也会在某个时候确实写入数据。这可能会受到写入数据量的影响,在您的情况下,写入的数据量似乎是中等大小的 600000 字节。
磁盘查找并不是磁盘 I/O(相对)慢的唯一原因。即使是使用 SSD 的 I/O 也比仅使用内存的数据传输要慢。
除其他外,标准文件系统不仅仅是一个扁平的字节跨度。即使没有任何移动部件,仍然需要与文件系统的数据结构交互以确定写入位置,并在写入时更新它。通常希望该信息立即对其他进程可见,因此它不会通常无限期延迟。
但我不知道 [...] 写信给 管道:它不只是内核中某处的一块内存吗? 孩子能读出来吗?
除此之外还有更多,但这是一个合理的初步近似值。
既然如此,为什么比 写入文件的过程基本相同?
因为写入常规文件不基本相同。还有很多。
【讨论】:
谢谢;也许我确实有一些误解。让我们解决它们:对于#1,关于刷新到文件,我的概念基于@James Kanze 来自here 的答案和最终评论。此外,如果文件和管道之间的区别还有很多,您能否指出一些关于此的内容? @Wad,正如 Kanze 所说,以同步模式打开文件将确保 eachwrite
写入的数据在 @987654325 之前传递到设备@调用返回。这并不意味着对于未在同步模式下打开的文件,永远不会有任何延迟或内核启动的刷新。
@Wad,对异地资源的请求在此不在主题范围内,但我已经对主要相关差异进行了高级视图。特别是,有一个实际的硬件设备可以在文件案例中与之交互,并且在此之上还有一个必须调解访问的文件系统。
是的,@Wad,我知道你没有使用这些标志。关键是您所指向的评论不支持您对案件行为的结论。它根本不涉及您的情况,只是可以公平地得出结论,那里的行为不知何故不同。
我怀疑您缺少的一点是您正在执行许多写入,而不仅仅是一个。在管道的情况下,接收器可以足够快地耗尽管道,从而最小化延迟写入。在文件案例中,如果(内核)I/O 缓冲区远小于您的总写入大小,您将被限制在内核可以将数据传输到磁盘以进行大部分传输的速度。以上是关于写入 Linux 管道的速度比文件快,但在内核级别,为啥?的主要内容,如果未能解决你的问题,请参考以下文章