open(2) 中的 O_SYNC 和 O_DIRECT 标志有何不同/相似?

Posted

技术标签:

【中文标题】open(2) 中的 O_SYNC 和 O_DIRECT 标志有何不同/相似?【英文标题】:How are the O_SYNC and O_DIRECT flags in open(2) different/alike? 【发布时间】:2011-06-30 15:54:49 【问题描述】:

O_SYNC 和 O_DIRECT 标志的使用和效果非常令人困惑,并且在平台之间似乎有所不同。在 Linux 手册页(参见示例 here)中,O_DIRECT 提供同步 I/O,最大限度地减少缓存影响并要求您自己处理块大小对齐。 O_SYNC 只是保证同步 I/O。尽管两者都保证将数据写入硬盘的缓存,但我相信直接 I/O 操作应该比普通同步 I/O 更快,因为它们绕过了页面缓存(尽管 FreeBSD 的 open(2) 手册页指出使用 O_SYNC 时会绕过缓存。请参阅here)。

O_DIRECT 和 O_SYNC 标志之间到底有什么区别?一些实现建议使用 O_SYNC | O_DIRECT。为什么?

【问题讨论】:

【参考方案1】:

O_DIRECT 仅承诺内核将避免将数据从用户空间复制到内核空间,而是通过 DMA 直接写入(直接内存访问;如果可能)。数据不会进入缓存。没有严格保证该函数只有在所有数据传输完毕后才会返回。

O_SYNC 保证在所有数据传输到磁盘之前调用不会返回(据操作系统所知)。这仍然不能保证数据不在硬盘写入缓存中的某个位置,但它是操作系统可以保证的。

O_DIRECT|O_SYNC 是这些的组合,即“DMA + 保证”。

【讨论】:

这个关于 O_SYNC 的答案是错误的。它确实保证数据已传输到介质。如果可用,内核将在写入时设置 FUA(强制单元访问)标志,或者它会发送单独的命令来刷新写入缓存。 @PaoloBonzini :包括 FUA 行为的 O_SYNC 取决于操作系统 - 例如早在 2013 年,Linux 就做到了,而 FreeBSD 没有(请参阅 Christoph 在serverfault.com/a/585427/303019 上的回答) @Anon:那将是 FreeBSD 中的一个错误。 @Damon:“成功传输”被定义为写入确保“所有写入的数据在文件的任何后续打开时都是可读的(即使是遵循系统或电源 故障)在没有物理存储介质故障的情况下。” (pubs.opengroup.org/onlinepubs/009695399/basedefs/…,强调我的)。所以 FreeBSD 确实 有一个错误——但即使它没有,它的 O_SYNC 实现也将完全无用,除非在具有非易失性缓存的磁盘上。 @Damon 我不会说它毫无用处(但你确实使用了限定符,所以我知道你知道 :-) 但我同意它的名字错误地暗示了“涡轮模式”它仅在利基场景中有所帮助。当内核缓存绝对不能帮助您时,它很有用,但如果您尝试使用它来提高速度,则必须添加磁盘如此快以至于您的 CPU 正在努力(相对)跟上的要求并且您可以将所有鸭子排成一排(对齐、足够/“足够大”的 I/O、完全配置等)。例如。我在***.com/a/48973798/2732969 中看到了好处。【参考方案2】:

有关 O_DIRECT 和 O_SYNC 的角色及其对数据完整性的影响的清晰描述,请参阅这篇 lwn 文章:

https://lwn.net/Articles/457667/

【讨论】:

【参考方案3】:

其实在linux 2.6下,o_direct是同步的,见man page:

manpage of open, there is 2 section about it..

2.4 以下不保证

O_DIRECT(自 Linux 2.4.10 起) 尽量减少进出该文件的 I/O 的缓存影响。一般来说,这会降低性能,但在特殊情况下很有用,例如当应用程序进行自己的缓存时。文件 I/O 直接从用户空间缓冲区完成。 O_DIRECT 标志自己努力同步传输数据,但不提供 O_SYNC 标志的保证,即传输数据和必要的元数据。为了保证同步 I/O,除了 O_DIRECT 之外,还必须使用 O_SYNC。有关进一步讨论,请参阅下面的注释。

raw(8) 中描述了块设备的语义相似(但已弃用)的接口。

但在 2.6 以下是有保证的,请参阅

O_DIRECT

O_DIRECT 标志可能会对用户空间缓冲区的长度和地址以及 I/O 的文件偏移施加对齐限制。在 Linux 中,对齐限制因文件系统和内核版本而异,并且可能完全不存在。然而,目前没有独立于文件系统的接口供应用程序发现给定文件或文件系统的这些限制。一些文件系统为此提供了自己的接口,例如 xfsctl(3) 中的 XFS_IOC_DIOINFO 操作。

在 Linux 2.4 下,传输大小,以及用户缓冲区和文件偏移的对齐方式都必须是文件系统逻辑块大小的倍数。在 Linux 2.6 下,对齐到 512 字节边界就足够了。

O_DIRECT I/O 绝不应该与 fork(2) 系统调用同时运行,如果内存缓冲区是私有映射(即,使用 mmap(2) MAP_PRIVATE 标志创建的任何映射;这包括在堆和静态分配的缓冲区)。任何此类 I/O,无论是通过异步 I/O 接口还是从进程中的另一个线程提交,都应在调用 fork(2) 之前完成。如果不这样做,可能会导致父进程和子进程中的数据损坏和未定义的行为。当 O_DIRECT I/O 的内存缓冲区是使用带有 MAP_SHARED 标志的 shmat(2) 或 mmap(2) 创建的时,此限制不适用。当使用 madvise(2) 将内存缓冲区建议为 MADV_DONTFORK 时,此限制也不适用,确保在 fork(2) 之后它对子级不可用。

O_DIRECT 标志是在 SGI IRIX 中引入的,它具有类似于 Linux 2.4 的对齐限制。 IRIX 还有一个 fcntl(2) 调用来查询适当的对齐方式和大小。 FreeBSD 4.x 引入了同名标志,但没有对齐限制。

在 Linux 内核版本 2.4.10 中添加了 O_DIRECT 支持。较旧的 Linux 内核只是忽略此标志。某些文件系统可能没有实现该标志,并且如果使用 open() 将失败并显示 EINVAL。

应用程序应避免将 O_DIRECT 和普通 I/O 混合到同一文件中,尤其是同一文件中的重叠字节区域。即使文件系统在这种情况下正确处理了一致性问题,总体 I/O 吞吐量也可能比单独使用任一模式慢。同样,应用程序应避免将具有直接 I/O 的文件的 mmap(2) 混合到相同的文件中。

使用 NFS 的 O_DIRECT 的行为将不同于本地文件系统。较旧的内核或以某些方式配置的内核可能不支持这种组合。 NFS 协议不支持将标志传递给服务器,因此 O_DIRECT I/O 只会绕过客户端上的页面缓存;服务器可能仍会缓存 I/O。客户端要求服务器使 I/O 同步以保留 O_DIRECT 的同步语义。在这些情况下,某些服务器的性能会很差,尤其是在 I/O 大小很小的情况下。一些服务器也可能被配置为向客户端谎称 I/O 已达到稳定存储;这将避免在服务器电源故障时对数据完整性造成一定风险的性能损失。 Linux NFS 客户端对 O_DIRECT I/O 没有对齐限制。

总之,O_DIRECT 是一个潜在的强大工具,应谨慎使用。建议应用程序将 O_DIRECT 的使用视为默认禁用的性能选项。

“O_DIRECT 一直困扰着我的一点是,整个界面简直是愚蠢的,而且可能是由一只发疯的猴子在一些严重的精神控制物质上设计的。”---Linus

【讨论】:

哪句话说2.6下保证同步?【参考方案4】:

AFAIK,O_DIRECT 绕过页面缓存。 O_SYNC 使用页面缓存但会立即同步它。页面缓存在进程之间共享,因此如果有另一个进程正在处理没有 O_DIRECT 标志的同一文件,则可以读取正确的数据。

【讨论】:

以上是关于open(2) 中的 O_SYNC 和 O_DIRECT 标志有何不同/相似?的主要内容,如果未能解决你的问题,请参考以下文章

[03]APUE:文件 I/O

操作系统升级后 mmap 行为改变了吗?

/dev/mem可没那么简单

Android平台到底能不能通过串口发送AT指令呢,急!!!

jffs2 Linux 文件系统上的 O_DIRECT 支持

删除指定目录下的文件及子文件