POSIX如何做O_DIRECT?

POSIX way to do O_DIRECT?

提问人:An Ant 提问时间:1/7/2023 更新时间:1/19/2023 访问量:770

问:

直接 I/O 是复制更大文件的最高性能方式,因此我想将这种功能添加到程序中。

Windows 提供 和 Win32 的 CreateFileA()。从 2.4.10 开始,Linux 具有 O_DIRECT 标志FILE_FLAG_WRITE_THROUGHFILE_FLAG_NO_BUFFERINGopen()

有没有办法在POSIX中可移植地实现相同的结果?就像这里的 Win32 API 从 Windows XP 到 Windows 11 的工作方式一样,以一种可靠的可移植方式在所有类 UNIX 系统上执行直接 IO 会很好。

C Linux 文件-IO IO POSIX

评论

3赞 n. m. could be an AI 1/7/2023
如果是这样的话,我们为什么需要一个特定于 Linux 的标志呢?
0赞 An Ant 1/7/2023
@n我想知道这是否可能(即使以某种迂回的方式,没有简单的基于开放的 API)。
0赞 Nadeem Taj 1/20/2023
可能重复。stackoverflow.com/questions/15971746/......

答:

3赞 Kibeth 1/17/2023 #1

简短的回答是否定的。

IEEE 1003.1-2017(当前的 POSIX 标准 afaik)没有提到任何直接 I/O 的指令,例如 .话虽如此,粗略地看一眼就知道 GNU/Linux 和 FreeBSD 支持这个标志,而 OpenBSD 则不支持。O_DIRECTO_DIRECT

除此之外,似乎不是所有的文件系统都支持,所以即使在 GNU/Linux 系统上,你知道你的实现会识别该指令,仍然不能保证你可以使用它。O_DIRECTopen()

归根结底,我能看到可移植的直接 I/O 的唯一方法是运行时检查您的程序运行的平台是否支持它;您可以进行编译时检查,但我不建议这样做,因为文件系统可能会更改,或者您的目标可能不在操作系统驱动器上。你可能会非常幸运地找到一个已经开始这样做的项目,但我有点怀疑它是否存在。

我对你的建议是,从编写程序开始,检查你的平台是否支持直接的 I/O,并采取相应的行动,添加对你知道你的程序将在其上运行的内核和文件系统的检查和支持。

希望我能得到更多的帮助,

2-δ

评论

1赞 Andrew Henle 1/17/2023
请注意,各种 Linux 文件系统对直接 IO 的支持级别可能会有所不同。大多数都需要页面对齐的缓冲区,但 IME 某些只会通过直接 IO 执行全页面大小的读取/写入,这使得读取或写入任何不是系统页面大小倍数的文件变得更加复杂。
4赞 Andrew Henle 1/17/2023 #2

不,没有直接 IO 的 POSIX 标准。

截至 2023 年 1 月,至少存在两种不同的 API 和行为。Linux、FreeBSD 和 IBM 的 AIX 都使用一个标志来 ,而 Oracle 的 Solaris 则在已经打开的文件描述符上使用一个函数。O_DIRECTopen()directio()

Linux open() 手册页记录了 Linux 对 POSIX open() 函数的标志的使用:O_DIRECT

O_DIRECT(从 Linux 2.4.10 开始)

尽量减少进出 thishttps://man7.org/linux/man-pages/man2/open.2.html 的 I/O 对缓存的影响 文件。一般来说,这会降低性能,但确实如此 在特殊情况下很有用,例如 https://en.wikipedia.org/wiki/QFSwhen 应用程序 他们自己的缓存。文件 I/O 直接进出 用户空间缓冲区。这面旗帜本身就形成了一个 努力同步传输数据,但没有给出 保证标志的数据和必要的 传输元数据。为了保证同步 I/O, 除了 O_SYNC 之外,还必须使用 。请参阅注释 下面进一步讨论。O_DIRECTO_SYNCO_DIRECT

Linux 没有明确指定直接 IO 如何与在同一文件上打开的其他描述符交互,或者当文件映射时会发生什么;对直接 IO 读取或写入操作也没有任何对齐或大小限制。根据我的经验,这些都是特定于文件系统的,并且随着时间的推移一直在改进/变得不那么严格,但大多数 Linux 文件系统都需要页面对齐的 IO 缓冲区,而且许多(大多数?(做过吗?现在还做吗?)需要页面大小的读取或写入。mmap()

FreeBSD 遵循 Linux 模型: O_DIRECT 标志传递给 open():

O_DIRECT可用于最小化或消除缓存效应 阅读和写作。系统将尝试避免缓存 数据 你 读取或写入。如果它无法避免缓存数据,它将最小化 数据对缓存的影响。如果不小心使用,使用此标志会大大降低性能。

OpenBSD 不支持直接 IO。在 OpenBSD open() 或 OpenBSD 'fcntl()' 手册页中都没有提到直接 IO。

IBM 的 AIX 似乎支持 linux 类型的 O_DIRECT 标志来 open(),但实际发布的 IBM AIX 手册页似乎并不普遍可用。

SGI 的 Irix 还支持 Linux 风格的 O_DIRECT 标志来 open():

O_DIRECT

如果设置,则对生成的文件描述符的所有读取和写入都将 直接在用户程序缓冲区中执行或从用户程序缓冲区执行,提供 满足适当的尺寸和对齐限制。请参阅手动条目中的 and 命令 有关如何确定对齐约束的信息。 是 Silicon Graphics 扩展,仅在 本地 EFS 和 XFS 文件系统,以及远程 BDS 文件系统。F_SETFLF_DIOINFOfcntl(2)O_DIRECT

有趣的是,Linux 上的 XFS 文件系统起源于 SGI 的 Irix。

Solaris 使用完全不同的接口。Solaris 使用特定的 directio() 函数来设置每个文件的直接 IO

描述

该函数向系统提供有关 应用程序在访问 与 打开文件描述符 关联的文件。系统 使用此信息来帮助优化对文件数据的访问。 该函数对另一个函数的语义没有影响 对数据的操作,尽管它可能会影响其他数据的性能 操作。directio()fildesdirectio()

建议参数按文件保留;的最后一个调用者使用与 关联的文件为所有应用程序设置建议。directio()fildes

建议值在 中定义。<sys/fcntl.h>

DIRECTIO_OFF

应用程序在访问文件数据时获取默认系统行为。

当应用程序从文件中读取数据时,首先缓存数据 ,然后复制到应用程序的缓冲区中(请参阅)。如果系统检测到应用程序正在读取 系统将按顺序从文件异步“提前读取” 从文件到系统内存中,以便数据立即可用 用于下一个操作。read(2)read(2)

当应用程序将数据写入文件时,首先缓存数据 在系统内存中,并在以后写入设备(请参阅)。如果可能,系统会通过在内存页中缓存数据来提高操作性能。数据 复制到系统内存中,操作返回 立即到应用程序。稍后写入数据 异步到设备。如果可能,缓存的数据是 “聚集”成大块,并在单个中写入设备 写入操作。write(2)write(2)write(2)

的系统行为可能会更改,恕不另行通知。DIRECTIO_OFF

DIRECTIO_ON

系统的行为就好像应用程序不会重用 文件数据在不久的将来。换言之,文件数据不是 缓存在系统的内存页中。

如果可能,数据直接在 应用程序的内存和设备在访问数据时使用和操作。当此类转移不是 可能,系统切换回默认行为,但只是 对于该操作。一般来说,当 应用程序的缓冲区在双字节(短)边界上对齐,即 偏移到文件中是在设备扇区边界上,并且大小 该操作是设备扇区的倍数。read(2)write(2)

当与 映射(请参阅)。fildesmmap(2)

的系统行为可能会更改,恕不另行通知。DIRECTIO_ON

另请注意,Solaris 上的行为是不同的:如果任何进程在文件上启用了直接 IO,则访问该文件的所有进程都将通过直接 IO 执行此操作(Solaris 10+ 对直接 IO 没有对齐或大小限制,因此在直接 IO 和“正常”IO 之间切换不会中断任何内容。如果文件是通过 映射的,则完全禁用该文件上的直接 IO。mmap()

* - 这并不完全正确 - 如果您在共享模式下使用 SAMFS 或 QFS 文件系统,并从文件系统的活动元数据控制器访问数据(其中文件系统必须按照设计使用 Solaris 挂载选项进行挂载,因此所有访问都通过集群中那个系统上的直接 IO 完成),如果您使用 禁用文件的直接 IO , 您将损坏文件系统。如果您在 QFS 元数据控制器上执行数据库还原,Oracle 自己的高端 RAC 数据库将执行此操作,并且最终会得到损坏的文件系统。forcedirectiodirectio( fd, DIRECTIO_OFF )