1. 正如你从研究中得出的结论一样,fflush
将用户空间缓冲区的数据与内核级别的缓存同步(由于它使用在用户级别上存在但对内核不可见的 FILE
对象),而 fsync
或 sync
(直接与文件描述符一起工作)将内核缓存的数据与设备同步。然而,后者并不保证数据已经实际写入存储设备——因为这些设备通常也带有自己的缓存。我认为,使用 MS_SYNC
标志调用的 msync
也可能存在相同的情况。
相关地,当谈论该主题时,我发现区分 同步 和 同步式 操作非常有用。以下是 Robert Love 简明扼要的解释:
同步写操作不会返回,直到被写入的数据至少被存储在内核的缓冲区中。[...] 同步操作比仅仅同步操作更加严格和安全。同步写操作将数据刷新到磁盘,确保磁盘上的数据始终与相应的内核缓冲区同步。
有了这个理解,你可以使用 O_SYNC
标志调用 open
(连同某些具有写权限的其他标志)来强制进行同步写操作。同样,正如你正确地假设的那样,这只能在启用了 WRITE THROUGH
磁盘缓存策略时工作,这实际上等于 禁用 磁盘缓存。
你可以阅读关于如何在 Linux 上禁用磁盘缓存的 这个回答。确保还要查看 这个网站,它除了 ATA 设备外,还涵盖了基于 SCSI 的设备(有关不同类型的磁盘的信息,请参见 Microsoft SQL Server 2005 的 此页面,最后更新时间为:2018 年 4 月 19 日)。
说到这里,阅读一下关于如何在 Windows 机器 上处理该问题非常有启发性:
要为无缓冲 I/O 打开文件,请使用 FILE_FLAG_NO_BUFFERING 和 FILE_FLAG_WRITE_THROUGH 标志调用 CreateFile 函数。这将防止文件内容被缓存,并在每次写入时刷新元数据到磁盘。有关详细信息,请参见 CreateFile。
显然,这是如何确保数据完整性的
Microsoft SQL Server 2005家族:
所有版本的SQL Server都使用Win32 CreateFile函数打开日志和数据文件。当被SQL Server打开时,dwFlagsAndAttributes成员包括FILE_FLAG_WRITE_THROUGH选项。[...]此选项指示系统通过任何中间缓存直接写入磁盘。系统仍然可以缓存写操作,但不能懒惰地刷新它们。
我特别提到这一点是因为2012年博客文章显示某些SATA磁盘忽略FILE_FLAG_WRITE_THROUGH!我不知道现在的情况如何,但似乎要确保写入磁盘真正同步,您需要:
- 使用设备驱动程序禁用磁盘缓存。
- 确保您正在使用的特定设备支持write-through / no-caching策略。
但是,如果您正在寻找数据完整性的保证,则可以购买具有超越电容器(通常仅足以完成正在进行的写入过程)的电池供电的磁盘。正如上面提到的博客文章中的结论:
底线,为您的数据和事务日志文件使用Enterprise-Class磁盘。[...]实际上,情况并不像看起来那么严重。许多RAID控制器具有带电池支持的缓存,并且不需要遵守写入要求。
2.为了部分回答第二个问题,这是来自man页面的内容,SYNC(2)
:
根据标准规范(例如,POSIX.1-2001),sync()调度写入,但可能在实际写入完成之前返回。但是,自版本1.3.20 Linux确实在等待。(这仍然不能保证数据完整性:现代磁盘具有大缓存。)
这将意味着fsync
和sync
工作方式不同,但请注意它们都在unistd.h
中实现,这表明它们之间存在一些一致性。但是,我会遵循Robert Love的建议,在编写自己的代码时不建议使用sync系统调用。
sync()的唯一真正用途是在sync实用程序的实现中。应用程序应使用fsync()和fdatasync()来提交所需文件描述符的数据到磁盘。请注意,在繁忙的系统上,sync()可能需要几分钟或更长时间才能完成。