刷新磁盘写缓存

Posted

技术标签:

【中文标题】刷新磁盘写缓存【英文标题】:Flush disk write cache 【发布时间】:2010-09-15 11:19:24 【问题描述】:

当 Windows XP 和 Vista 中的磁盘策略设置为在硬盘上启用写入缓存时,有没有办法刷新刚刚写入的文件,并确保它已提交到磁盘?

我想在 C++ 中以编程方式执行此操作。

关闭文件确实会在应用程序级别执行刷新,但不会在操作系统级别执行。如果在关闭文件后,但在操作系统刷新磁盘写入缓存之前断开 PC 的电源,则即使文件已关闭,文件也会丢失。

【问题讨论】:

你的意思是编程吗?这个问题没有说清楚 我们假设您的问题是关于编程的,但您没有指定开发环境。 【参考方案1】:

.NET FileStream.Flush() 不会刷新该文件内容的 Windows 缓存; Flush() 仅刷新 .NET 内部文件缓冲区。在 .NET 4.0 中,Microsoft 通过向 Flush() 添加一个可选参数来解决此问题,如果设置为 true,则会调用 FlushFileSystemBuffers。在 .NET 3.5 及以下版本中,您唯一的选择是通过 pinvoke 调用 FlushFileBuffers。请参阅 MSDN 的FileStream.Flush 社区评论了解如何执行此操作。

【讨论】:

警告:.Net 4.0 Flush(true) 不能修复它!!!! MS 错误报告here 说 file.Flush(true) 已损坏,然后修复,但没有说明它是在哪个版本或服务包中修复的!听起来错误是如果内部 .NET FileStream 缓冲区为空,则 Flush(true) 什么也没做??【参考方案2】:

您不应该在关闭文件时修复此问题。 Windows 将缓存,除非您打开将FILE_FLAG_WRITE_THROUGH 传递给 CreateFile() 的文件。

您可能还想传递FILE_FLAG_NO_BUFFERING;这告诉 Windows 不要在缓存中保留字节的副本。

根据 MSDN 上的 CreateFile 文档,这比 FlushFileBuffers() 更有效。

另请参阅 MSDN 上的 file buffering 和 file caching。

【讨论】:

【参考方案3】:

你还没有指定开发环境,所以:

.Net

IO 流有一个 .Flush 方法,可以满足您的需求。

Win32 API

FlushFileBuffers 调用,它将文件句柄作为参数。

编辑(基于 OA 的评论):FlushFileBuffers 不需要管理权限;只有当传递给它的句柄是卷的句柄,而不是单个文件的句柄时,它才会这样做。

【讨论】:

这似乎可行。不幸的是,应用程序必须能够在没有管理权限的情况下运行。 我找不到这个 .NET 方法。据我所知,框架使用 FlushFileBuffers 的唯一地方是 SerialStream。我怀疑 p/invoking FlushFileBuffers 是安全的选择。 我发现通过 pInvoke 调用 FlushFileBuffers 会导致异常(***.com/q/9195807/4540)。在 .NET 4 下,按照@jimvfr 的建议 (***.com/a/3992428/4540) 调用 FileStream.Flush(true) 会更容易、更安全。【参考方案4】:

您还应该注意,您的数据可能不会被刷新到实际磁盘,即使在调用框架 API 的刷新方法时也是如此。

调用 flush 方法只会告诉内核将其页面刷新到磁盘。但是,如果您打开了磁盘写入缓存,则可以无限期地延迟实际的写入过程。

为了确保您的数据被写入物理层,您必须关闭操作系统中的写入缓存。在处理大量小型 io 操作时,这通常会导致性能损失高达一到两个数量级。 基于电池的支持 (UPS) 或接受命令以刷新磁盘写入缓存的磁盘是解决此问题的另一种选择。

【讨论】:

Windows 将强制刷新重要数据的磁盘缓存,除非磁盘策略已设置为“关闭 Windows 写入缓存缓冲区刷新” 当然会。但是,如果打开硬盘的写入缓存,则磁盘可能会停止写入操作。阅读,例如:support.microsoft.com/kb/259716/EN-US 引用:“通过启用写入缓存,如果机器遇到电源、设备或系统故障并且无法正常关闭,则可能会发生文件系统损坏和/或数据丢失。” 实际上,无论是否打开了写入缓存缓冲,即使打开了写入缓存刷新(以前称为“高级性能复选框”),Windows 都会在 FlushFileBuffers() 上强制刷新缓存。 【参考方案5】:

从微软文档中,您将使用_flushall 并在 COMMODE.OBJ 中进行链接,以确保所有缓冲区都已提交到磁盘。

【讨论】:

我们还必须在 fopen() 期间添加“c”模式选项【参考方案6】:

请看这里:https://jeffpar.github.io/kbarchive/kb/066/Q66052/

当您最初使用 fopen 打开文件时,将“c”模式选项作为最后一个选项:

fopen( path, "wc") // w - write mode, c - allow immediate commit to disk

然后当你想强制刷新到磁盘时,调用

_flushall()

我们在打电话之前打了这个电话

fclose()

我们遇到了您描述的确切问题,这种方法解决了它。

请注意,这种方法不需要像其他人提到的那样,FlushFileBuffers 确实需要管理权限。

来自上述网站:

“Microsoft C/C++ 7.0 版为 fopen() 引入了“c”模式选项 功能。当应用程序打开文件并指定“c”模式时, 运行时库将文件缓冲区的内容写入磁盘,当 应用程序调用 fflush() 或 _flushall() 函数。 "

【讨论】:

以上是关于刷新磁盘写缓存的主要内容,如果未能解决你的问题,请参考以下文章

从 Windows CLI 刷新磁盘写入缓存

如何在 C# 中清空/刷新 Windows READ 磁盘缓存?

python 写文件刷新缓存

SQL Server 了解日志缓存刷新

SQL Server 了解日志缓存刷新

几个io术语