POSIX的O_DIRECT实现方式?

qrjkbowd  于 2023-01-20  发布在  其他
关注(0)|答案(2)|浏览(165)

bounty将在15小时后过期。回答此问题可获得+50声望奖励。An Ant希望引起更多人关注此问题。

直接I/O是复制较大文件的性能最高的方法,因此我想将此功能添加到程序中。
Windows在Win32的CreateFileA()中提供了FILE_FLAG_WRITE_THROUGHFILE_FLAG_NO_BUFFERING。Linux从2.4.10开始提供了O_DIRECT flag
有没有一种方法可以在POSIX中实现同样的结果?就像这里的Win32 API如何从Windows XP到Windows 11工作一样,以一种可靠的可移植方式在所有类似UNIX的系统中进行直接IO是很好的。

yhxst69z

yhxst69z1#

简短的回答是否定的。
IEEE 1003.1 - 2017(当前的POSIX标准afaik)没有提到任何像O_DIRECT这样的直接I/O指令,尽管如此,粗略地看一下就知道GNU/Linux和FreeBSD支持O_DIRECT标志,而OpenBSD不支持。
除此之外,似乎不是所有的文件系统都支持O_DIRECT,所以即使在GNU/Linux系统上,您知道您的open()实现将识别该指令,仍然不能保证您可以使用它。
在一天结束的时候,我能看到可移植的、直接I/O的唯一方法是运行时检查您的程序运行的平台是否支持它;你可以做编译时检查,但我不推荐这样做,因为文件系统可能会改变,或者你的目标可能不在操作系统驱动器上。你可能会超级幸运地找到一个已经开始做这件事的项目,但我有点怀疑它是否存在。
我的建议是,从编写程序开始,检查平台是否支持直接I/O并采取相应的措施,增加对内核和文件系统的检查和支持,您知道您的程序将在这些系统上运行。
希望我能帮上忙,

  • -K
c9x0cxw0

c9x0cxw02#

不,没有针对直接IO的POSIX标准。
截至2023年1月,至少存在两种不同的API和行为,Linux、FreeBSD和IBM的AIX显然都使用O_DIRECT标记来标记open(),而Oracle的Solaris则在已经打开的文件描述符上使用directio()函数。
the Linux open() man page中记录了Linux对POSIX open() function使用O_DIRECT标志的情况:
O_DIRECT(自Linux 2.4.10起)
尝试最大限度地减少此shttps://www.example.com文件的I/O缓存效应。通常,这会降低性能,但在特殊情况下很有用,例如https://en.wikipedia.org/wiki/QFSwhen应用程序执行自己的缓存。文件I/O直接执行到用户空间缓冲区或从用户空间缓冲区执行。O_DIRECT标志本身致力于同步传输数据。man7.org/linux/man-pages/man2/open.2.html file. In general this will degrade performance, but it is useful in special situations, such as https://en.wikipedia.org/wiki/QFSwhen applications do their own caching. File I/O is done directly to/from user-space buffers. The O_DIRECT flag on its own makes an effort to transfer data synchronously, but does not give the guarantees of the O_SYNC flag that data and necessary metadata are transferred. To guarantee synchronous I/O, O_SYNC must be used in addition to O_DIRECT . See NOTES below for further discussion.
Linux没有明确指定直接IO如何与在同一文件上打开的其他描述符交互,或者当使用mmap()Map文件时会发生什么;根据我的经验,这些都是特定于文件系统的,并且随着时间的推移已经得到改进/变得不那么严格,但是大多数Linux文件系统需要页面对齐的IO缓冲区,并且许多(大多数?所有?)(过去?现在?仍然?)需要页面大小的读取或写入。
FreeBSD遵循Linux模式:passing an O_DIRECT flag to open()
O_DIRECT可用于最小化或消除读取和写入的缓存影响。系统将尝试避免缓存您读取或写入的数据。如果无法避免缓存数据,它将最小化数据对缓存的影响。如果不小心使用,使用此标志可能会显著降低性能。
OpenBSD不支持直接IO,在the OpenBSD open()或[the OpenBSD 'fcntl()](https://man.openbsd.org/fcntl)手册页中都没有提到直接IO。 IBM的AIX [appears to support a Linux-typeO_DIRECTflag toopen()](https://www.ibm.com/docs/en/spectrum-scale/5.1.6?topic=applications-considerations-use-direct-io-o-direct),但实际发布的IBM AIX手册页似乎并不普遍可用。 SGI的Irix也是[supported the Linux-styleO_DIRECTflag toopen()](https://wiki.preterhuman.net/Direct_I/O_with_C%2B%2B_on_SGI_IRIX):O_DIRECT如果设置,则对结果文件描述符的所有读取和写入将直接执行到用户程序缓冲区或从用户程序缓冲区执行,前提是满足适当的大小和对齐限制。有关如何确定对齐限制的信息,请参阅fcntl(2)手动条目中的F_SETFLF_DIOINFO命令。O_DIRECT是Silicon Graphics扩展,仅在本地EFS和XFS文件系统以及远程BDS文件系统上受支持。 有趣的是,Linux上的XFS文件系统起源于SGI的Irix。 Solaris使用完全不同的接口,它使用特定的directio()`函数来设置每个文件的直接IO:

    • 说明**

directio()函数向系统提供有关访问与打开文件描述符fildes关联的文件中的数据时应用程序的预期行为的建议。系统使用此信息帮助优化对文件数据的访问。directio()函数对数据上的其他操作的语义没有影响。尽管它可能影响其它操作的性能。
advice参数保存在每个文件中;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)操作访问数据时,将直接在应用程序的内存和设备之间读取或写入数据。如果无法进行此类传输,系统将切换回默认行为,但仅限于该操作。通常,当应用程序的缓冲区在双字节(短)边界,文件中的偏移位于设备扇区边界上,并且操作的大小是设备扇区的倍数。
Map与fildes关联的文件时将忽略此建议(请参见mmap(2))。
DIRECTIO_ON的系统行为可能会更改,恕不另行通知。
另请注意,Solaris上的行为有所不同:如果任何进程在某个文件上启用了直接IO,则访问该文件的 * 所有 * 进程都将通过直接IO执行此操作(Solaris 10+对直接IO没有对齐或大小限制,因此在直接IO和“正常”IO之间切换不会造成任何中断。*)。如果通过mmap()Map某个文件,则将完全禁用该文件上的直接IO。

    • 如果您在共享模式下使用SAMFS or QFS filesystem,并从文件系统的活动元数据控制器访问数据,那么这并不完全正确(其中,必须使用Solaris forcedirectio挂载选项 * 按设计 * 挂载文件系统,以便所有访问都通过群集中该系统上的直接IO完成),如果使用directio( fd, DIRECTIO_OFF )禁用文件的直接IO,如果您在QFS元数据控制器上执行数据库恢复,Oracle自己的顶级RAC数据库将执行此操作,并且您将得到一个损坏的文件系统。

相关问题