缓冲与非缓冲 IO

Posted

技术标签:

【中文标题】缓冲与非缓冲 IO【英文标题】:Buffered vs unbuffered IO 【发布时间】:2010-11-29 20:23:38 【问题描述】:

我了解到,默认情况下,程序中的 I/O 是缓冲的,即它们是从临时存储中提供给请求程序的。 我知道缓冲可以提高 IO 性能(可能是通过减少系统调用)。我见过禁用缓冲的例子,比如 C 语言中的setvbuf。这两种模式有什么区别,什么时候应该使用另一种模式?

【问题讨论】:

【参考方案1】:

当您已经准备好要写入磁盘的大量字节序列并且想要避免额外复制到中间的第二个缓冲区时,您需要无缓冲输出。

缓冲的输出流会将写入结果累积到中间缓冲区中,仅当累积了足够的数据(或请求flush())时才将其发送到操作系统文件系统。这减少了文件系统调用的数量。由于在大多数平台上文件系统调用可能很昂贵(与短 memcpy 相比),因此在执行大量小型写入时,缓冲输出是一个净赢。当您已经有大缓冲区要发送时,无缓冲输出通常会更好 - 复制到中间缓冲区不会进一步减少操作系统调用的数量,并且会引入额外的工作。

无缓冲输出与确保数据到达磁盘无关;该功能由flush() 提供,适用于缓冲和非缓冲流。无缓冲的 IO 写入并不能保证数据已到达物理磁盘——操作系统文件系统可以无限期地保留数据的副本,如果需要,永远不会将其写入磁盘。只有在调用flush() 时才需要将其提交到磁盘。 (请注意,close() 将代表您致电 flush())。

【讨论】:

调用flush() 会保证它已写入磁盘吗?我以为只会将它传递到磁盘的缓冲区。 您需要O_SYNC 以确保保证写入。 无缓冲 IO 被写入磁盘。因此,对于 winapi,您使用 FILE_FLAG_NO_BUFFERING 和 FILE_FLAG_WRITE_THROUGH 调用 CreateFile 的术语无缓冲(没有中间缓冲区但直接写入磁盘)以确保数据在每次写入后直接进行持久化。对于其他一些不知道的操作系统。【参考方案2】:

只要您想确保在继续之前已写入输出,就需要无缓冲的输出。一个例子是 C 运行时库下的标准错误 - 默认情况下通常没有缓冲。由于错误(希望)很少发生,因此您想立即了解它们。另一方面,标准输出 被缓冲只是因为它假设会有更多的数据通过它。

另一个例子是日志库。如果您的日志消息保存在进程的缓冲区中,并且您的进程转储核心,则很有可能永远不会写入输出。

此外,不仅系统调用被最小化,磁盘 I/O 也被最小化。假设一个程序一次读取一个字节的文件。使用无缓冲输入,您将访问(相对非常慢的)磁盘的每个字节,即使它可能必须读取整个块(磁盘硬件本身可能有缓冲区,但您仍然要访问磁盘控制器这将比内存访问慢)。

通过缓冲,整个块被立即读入缓冲区,然后各个字节从(内存中,非常快的)缓冲区传递给您。

请记住,缓冲可以采用多种形式,例如以下示例:

+-------------------+-------------------+
| Process A         | Process B         |
+-------------------+-------------------+
| C runtime library | C runtime library | C RTL buffers
+-------------------+-------------------+
|               OS caches               | Operating system buffers
+---------------------------------------+
|      Disk controller hardware cache   | Disk hardware buffers
+---------------------------------------+
|                   Disk                |
+---------------------------------------+

【讨论】:

图表很棒。值得一提的是FILE 对象(流)的内部缓冲区与fgets 所需的缓冲区参数完全不同。这让我困惑了好几个小时,然后才写了一些代码来弄清楚。问答

以上是关于缓冲与非缓冲 IO的主要内容,如果未能解决你的问题,请参考以下文章

Java-NIO:直接缓冲区与非直接缓冲区

Java NIO -- 直接缓冲区与非直接缓冲区

zbb20180930 java,nio直接缓冲区与非直接缓冲区别

IO NIO

IO NIO

操作系统-IO零拷贝