fflush、fsync 和 sync 与内存层

fflush, fsync and sync vs memory layers

我知道已经有类似的问题了,我看了看,但找不到明确、明确的答案来回答我的问题。我只是在网上调查这些功能及其与内存层的关系。特别是我发现这个漂亮的 article 让我对内存层有了很好的了解

似乎fflush()将数据从应用程序移动到内核文件系统缓冲区,没关系,每个人似乎都同意这一点。唯一让我感到困惑的是,在同一篇文章中,他们假设一个 write-back 缓存说 fsync() "the data is saved to the stable storage layer" 然后他们添加了 "the storage may itself store the data in a write-back cache, so fsync() is still required for files opened with O_DIRECT in order to save the data to stable storage"

阅读here and there it seems like the truth is that fsync() and sync() let the data enter the storage device but if this one has caching layers it is just moved here, not at once to permanent storage and data may even be lost if there is a power failure. Unless we have a filesystem with barriers enabled and then "sync()/fsync() and some other operations will cause the appropriate CACHE FLUSH (ATA) or SYNCHRONIZE CACHE (SCSI) commands to be sent to the device" [from your website answer]

问题:

  1. 如果要更新的数据已经在内核缓冲区中并且我的设备在 write-back 模式下具有易失性缓存层fsync() [和 sync() 我想] 将数据同步到稳定内存层,跳过易失性内存层?我认为这是 write-through 缓存发生的情况,而不是 write-back 缓存。根据我的阅读,我了解到 fsync() 上的 write-back 缓存只能将数据发送到将它们放入易失性缓存中的设备,并且它们只会在

    之后进入永久内存
  2. 我读到 fsync() 使用文件描述符,然后使用单个文件,而 sync() 导致缓冲区的总部署,因此它适用于每个数据更新。从这个 page 也可以看出 fsync() 等待写入磁盘的结束,而 sync() 不等待实际写入磁盘的结束。两者之间的内存数据传输还有其他差异吗?

感谢那些愿意提供帮助的人

"I don't have any solution, but certainly admire the problem."

我从你的好参考资料中读到的是,没有标准。该标准在内核的某处结束。内核控制设备驱动程序,设备驱动程序(可能由磁盘制造商提供)通过 API(设备上有小型计算机)控制磁盘。制造商可能已经添加了 capacitors/battery 刚好足够的电源以在电源出现故障时刷新其设备缓冲区,或者他可能没有。该设备可能提供同步功能,但是这是否真正同步(刷新)设备缓冲区是未知的(取决于设备)。因此,除非您 select 并根据您的规格安装设备(并验证这些规格),否则您永远无法确定。

这是一个公平的问题。即使在处理了错误情况之后,您仍然无法确保存储中存在的数据。

fsync 的手册页清楚地解释了这个问题!! :) 对于需要更严格保证完整性的应用程序 他们的数据,Mac OS X 提供了 F_FULLFSYNC fcntl。 F_FULLFSYNC fcntl 要求驱动器将所有缓冲数据刷新到永久存储。

需要严格的写入顺序的应用程序,例如数据库 应该使用 F_FULLFSYNC 来确保他们的数据按照他们期望的顺序写入。详情请参阅 fcntl(2)。

1. 正如您从研究中正确得出的结论 fflush 同步 user-space 缓冲的 数据到 内核级 缓存(因为它使用 FILE 位于用户级且对内核不可见的对象),而 fsyncsync(直接使用 文件描述符 )将内核缓存数据与设备同步。然而,后者并不能保证数据已实际写入存储设备——因为这些通常也带有自己的缓存。我希望用 MS_SYNC 标志调用 msync 也是如此。

相关地,我发现 synchronizedsynchronous 操作之间的区别在讨论该主题时非常有用。以下是 Robert Love 的简洁表述:

A synchronous write operation does not return until the written data is—at least—stored in the kernel’s buffer cache. [...] A synchronized operation is more restrictive and safer than a merely synchronous operation. A synchronized write operation flushes the data to disk, ensuring that the on-disk data is always synchronized vis-à-vis the corresponding kernel buffers.

考虑到这一点,您可以调用带有 O_SYNC 标志的 open(连同其他一些以写入权限打开文件的标志)来强制执行同步写入操作。同样,正如您正确假设的那样,这仅适用于 WRITE THROUGH 磁盘缓存策略,这实际上相当于 禁用 磁盘缓存。

您可以阅读 this answer about how to disable disk caching on Linux. Be sure to also check this website which also covers SCSI-based in addition to ATA-based devices (to read about different types of disks see this page on Microsoft SQL Server 2005,最后更新时间:2018 年 4 月 19 日)。

说到这里,在 Windows machines:

上阅读有关如何处理该问题的信息非常有用

To open a file for unbuffered I/O, call the CreateFile function with the FILE_FLAG_NO_BUFFERING and FILE_FLAG_WRITE_THROUGH flags. This prevents the file contents from being cached and flushes the metadata to disk with each write. For more information, see CreateFile.

显然,这就是 Microsoft SQL Server 2005 系列确保数据完整性的方式:

All versions of SQL Server open the log and data files using the Win32 CreateFile function. The dwFlagsAndAttributes member includes the FILE_FLAG_WRITE_THROUGH option when opened by SQL Server. [...] This option instructs the system to write through any intermediate cache and go directly to disk. The system can still cache write operations, but cannot lazily flush them.

我说这特别有用,因为 blog post from 2012 显示某些 SATA 磁盘 忽略 FILE_FLAG_WRITE_THROUGH!不知道现在的情况如何,不过好像要保证写磁盘是真正同步的,需要:

  1. 使用您的设备驱动程序禁用磁盘缓存。
  2. 确保您使用的特定设备支持 write-through/no-caching 政策。

但是,如果您正在寻找数据完整性的保证,您可以只购买一个磁盘,它有自己的基于电池的电源,而不是电容器(通常只够完成正在进行的写入过程).正如上面提到的博客文章中的结论:

Bottom-line, use Enterprise-Class disks for your data and transaction log files. [...] Actually, the situation is not as dramatic as it seems. Many RAID controllers have battery-backed cache and do not need to honor the write-through requirement.

2. 要(部分)回答第二个问题,这是来自手册页 SYNC(2)

According to the standard specification (e.g., POSIX.1-2001), sync() schedules the writes, but may return before the actual writing is done. However, since version 1.3.20 Linux does actually wait. (This still does not guarantee data integrity: modern disks have large caches.)

这意味着 fsyncsync 的工作方式不同,但是请注意,它们都是在 unistd.h 中实现的,这表明它们之间存在一定的一致性。但是,我会跟随 Robert Love 不建议在编写自己的代码时使用 sync 系统调用。

The only real use for sync() is in the implementation of the sync utility. Applications should use fsync() and fdatasync() to commit to disk the data of only the requisite file descriptors. Note that sync() may take several minutes or longer to complete on a busy system.

是的,fflush() 确保数据离开进程内存 space,但它可能在 RAM 的脏页中等待写回。这是防止应用程序中止的证据,但不是系统崩溃或电源故障。即使有备用电源,系统也可能因某些软件漏洞而崩溃!正如在其他 answers/comments 中提到的,从磁性写入磁盘或任何 SSD 所做的脏页中获取数据,而不是卡在磁盘控制器或驱动器中的某个易失性缓冲区中,是正确调用或打开选项的组合正确的控制器和设备!调用使您可以更好地控制开销,在事务结束时批量写入更多内容。

例如,RDBMS 不仅需要担心数据库保存文件,还需要担心允许恢复的日志文件,无论是在磁盘丢失后还是在任何 RDBMS 崩溃后重新启动时。事实上,有些可能在日志中比数据库更同步,以保持速度,因为恢复不是一个频繁的过程,而且通常不会很长。如果日志完好无损,事务写入日志的内容保证可以恢复。