了解 fwrite() 的缓冲行为

Posted

技术标签:

【中文标题】了解 fwrite() 的缓冲行为【英文标题】:Understanding buffering behavior of fwrite() 【发布时间】:2012-05-05 20:57:36 【问题描述】:

我正在使用函数调用fwrite() 将数据写入 Linux 上的管道。

之前,fwrite() 被反复调用以获取小块数据(平均 20 字节),而缓冲留给了fwrite()。对进程的 strace 显示一次写入了 4096 字节的数据。

原来这个编写过程是我程序的瓶颈。所以我决定将代码中的数据缓冲成 64KB 的块,然后使用fwrite() 一次写入整个块。我使用setvbuf() 将 FILE* 指针设置为“无缓冲”。

性能提升没有我预期的那么显着。

更重要的是,strace 输出显示数据仍一次写入 4096 个字节。有人可以向我解释这种行为吗?如果我用 64KB 的数据调用 fwrite(),为什么它一次只写入 4096 个字节?

是否有替代fwrite() 使用 FILE* 指针将数据写入管道的方法?

【问题讨论】:

@Shailesh_Tainwala:您可能正在用 C++ 编写代码,但这是一个 C 问题,而不是 C++。 fwrite() 是一个 c 函数,而不是 c++ 函数。我在您的问题中添加了一个 c 标签,这样您就可以获得更广泛的受众。 【参考方案1】:

如果您想更改缓冲行为,您必须在 fopen 之后立即执行此操作(或在任何 I/O 之前,对于标准文件句柄 stdinstdoutstderr)。您也不想禁用缓冲并尝试自己管理缓冲区;相反,请将您的 64K 缓冲区指定为 setvbuf,以便正确使用它。

如果你真的想手动管理缓冲,不要使用stdio;使用较低级别的openwriteclose 调用。

【讨论】:

【参考方案2】:

4096 来自作为管道基础的 Linux 机器。它发生在两个地方。一是管道的容量。在旧版本的 Linux 上,容量是一个系统页面,在 32 位 i386 机器上是 4096 字节。 (在更现代的 Linux 版本上,容量为 64K。)

您将遇到 4096 字节问题的另一个地方是定义的常量PIPE_BUF,即保证被原子处理的字节数。在 Linux 上,这是 4096 字节。此限制的含义取决于您是否将管道设置为阻塞或非阻塞。为所有血腥细节发送man -S7 pipe

如果您尝试高速交换大量数据,您可能需要重新考虑使用管道。您在 Linux 机器上,因此可以选择共享内存。您可以使用管道作为信号机制来发送相对少量的数据。

【讨论】:

另一个可能的 Linux 唯一选项是使用 open() 和 splice() 而不是 fwrite()。

以上是关于了解 fwrite() 的缓冲行为的主要内容,如果未能解决你的问题,请参考以下文章

Linux 和 Windows 之间 fwrite 的不同行为

fwrite 消耗所有 MemFree,fflush 不起作用?

缓冲文件系统(fopen/fread/fwrite)和非缓冲文件系统(open/read/write)

缓冲文件系统(fopen/fread/fwrite)和非缓冲文件系统(open/read/write)

Linux read/write fread/fwrite两者区别

使用 fwrite 时使文件环绕