在Linux/FreeBSD上,O_DIRECT和O_SYNC有什么区别?

7
我正在编写一个可以在Linux和FreeBSD上运行的程序,并且我希望确保每次write()返回时,数据实际上已经被写入到物理设备上的文件中,以免因意外情况(如断电、进程意外中断等)导致我的数据丢失。
根据OPEN(2)手册,在Linux(高于2.6版本),O_DIRECT是同步的,但可能存在性能问题;在FreeBSD上,O_DIRECT不能保证同步,也可能存在问题。
因此,在Linux上,无论是O_DIRECT还是O_SYNC都可以保证同步写入,但哪个具有更好的性能呢?
在FreeBSD上,为了保证同步写入,哪种选项具有最佳性能:(1)O_DIRECT+fsync()、(2)O_DIRECT | O_SYNC或者(3)O_SYNC单独使用?

我认为你根本无法进行比较,因为它们是根本不同的东西(即使它们看起来似乎做了类似的事情)。 - Damon
3个回答

8

当前的硬盘存在一个问题,即使硬盘向操作系统报告写入完成,也无法保证文件实际上已经被写入到了硬盘中!这是由于硬盘内置缓存所导致的。

在FreeBSD系统中,你可以通过将kern.cam.ada.write_cache参数设置为0来关闭硬盘的缓存。但是这样做会显著地降低写入性能。我最近测试过(使用ICH-7芯片组、FreeBSD 8.1 AMD64系统和WDC WD5001ABYS-01YNA0硬盘),连续写入性能(使用dd if=/dev/zero of=/tmp/foo bs=10M count=1000命令进行测量)从每秒75,000,000字节下降到了每秒12,900,000字节。

如果你想要确保你的文件已经被写入:

  • 使用sysctl kern.cam.ada.write_cache=0命令关闭硬盘缓存,然后使用camcontrol reset <bus>:<target>:<lun>命令重置设备。
  • 使用O_SYNC选项打开文件。

注意:

  • 关闭硬盘缓存后,你的写入性能(在HDD硬盘上)将会非常糟糕。
  • 不要使用sync选项挂载分区,否则所有的I/O操作(包括读取)都将以同步方式进行。
  • 不要使用O_DIRECT选项。它会尝试完全绕过缓存。这可能也会影响读取操作。

3

O_DIRECT主要是为了让Oracle绕过内核的缓存层并进行自己的缓存而存在。它的语义定义不清,对读取的大小和对齐方式有任意限制,一般不建议使用。如果没有一个足够强大的文件系统来抵御电源故障或系统崩溃的影响,即使使用O_SYNC也可能无法满足你的需要。


关于性能方面,O_DIRECT和O_SYNC哪个性能更好?谢谢。 - Wang Tuma
除非你编写自己的缓存层(即使如此),O_DIRECT 的性能也极差,因为它每次都会从磁盘重新读取。 - R.. GitHub STOP HELPING ICE

1
所以,在Linux上,O_DIRECTO_SYNC都可以保证同步写入,但哪个性能更好?这种说法是不正确的,因为正如 @roland-smith提到的那样,至少在Linux上,O_DIRECT不能保证数据已经到达非易失性介质。它可能会在特定环境下(例如直接向具有电池备份SCSI控制器的磁盘表示块设备进行写入)提供该保证,但您不能在一般情况下依赖它(例如在仅由单个SATA硬盘支持的ext4文件系统中写入文件),原因如下:
  • 文件系统中的文件上的O_DIRECT不能保证已经写入检索数据所需的元数据,以便在断电崩溃后恢复数据
  • 内核在原始调用完成之前将I/O发送到硬件,但I/O仅在易失性硬件缓存中
在上述情况下,突然断电意味着您的程序认为I/O操作成功,但实际上并没有成功。如今,Linux open(2) man page中指出:

仅使用 O_DIRECT 标志会尝试同步传输数据,但不像 O_SYNC 标志那样保证数据和必要的元数据已经传输。为了保证同步I/O,必须同时使用 O_SYNCO_DIRECT 标志。有关详细讨论,请参见下面的注释。

在所给的场景下,在Linux上保证每次写入都是同步写入的唯一方法是使用O_SYNC(这会导致速度下降)或在每次I/O后执行fsync()(这可能会更慢,因为你执行了两个系统调用)。如果我担心速度,我会放弃使用O_SYNC,而是尽可能大地批量写入,然后在一个批次后执行fsync()。此外,请注意,如果您担心数据完整性,则必须检查所有fsync()和所有write()调用(以及close()等)的返回代码是否有错误。
请参阅此“O_DIRECT到底意味着什么?”的答案以获取更多详细信息和链接。

在FreeBSD上,为了保证同步写入,哪个选项具有最佳性能:(1)O_DIRECT+ fsync()、(2)O_DIRECT | O_SYNC或(3)O_SYNC

您处于与Linux类似的情况中(请参见上文),但在这三个选项中,我认为第三个选项(仅O_SYNC)会是最快的选择。FreeBSD open(2) man page关于O_DIRECT的说明如下:

可以使用O_DIRECT来最小化或消除读写缓存效应。系统将尝试避免缓存您读取或写入的数据。如果无法避免缓存数据,则将最小化数据对缓存的影响。若未谨慎使用此标志,可能会极大地降低性能。

一般注意事项:使用O_DIRECT并不意味着所有I/O都会变快——这取决于工作负载(I/O大小、I/O频率、I/O是顺序还是随机、同步和异步提交I/O的频率,因为它会影响合并等)以及I/O的提交方式(同步 vs 异步)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接