1

关于将文件活动刷新到磁盘的 Windows Win32 C++ 问题。

我有一个外部应用程序(使用 CreateProcess 运行)来创建一些文件。即,当它返回时,它将创建一个包含一些内容的文件。

在继续之前,如何确保进程创建的文件确实刷新到磁盘?

我的意思不是 C++ 缓冲区,而是真正刷新磁盘(例如 FlushFileBuffers)。

请记住,我无权访问任何文件 HANDLE - 这当然隐藏在外部进程中。

我想我可以打开我自己的文件句柄,然后使用 FlushFileBuffers,但不清楚这是否可行(因为我的句柄实际上不包含任何需要刷新的东西)。

最后,我希望它在非管理员用户空间中运行,因此我不能在整个卷上使用 FlushFileBuffers。

有任何想法吗?


更新:为什么我认为这是一个问题?

我正在开发数据备份应用程序。本质上,它必须按照描述创建一些文件。然后它必须更新它的内部数据库(使用 SQLite 嵌入式数据库)。

我最近遇到了蓝屏期间发生的数据损坏问题(其原因与我的应用程序无关)。

我关心的是系统崩溃期间的应用程序完整性。是的,我确实关心这一点,因为这个应用程序是一个数据备份应用程序。

我关心的用例是这样的:

  1. 使用外部进程创建一个小数据文件。此写入在 OS 缓存中等待写入磁盘。
  2. 我更新数据库并提交。这是磁盘活动。此写入也在 OS 缓存中等待。
  3. 发生系统故障。

在我看来,我们现在处于潜在的竞争状态。如果“1”被刷新而“2”没有被刷新,那么我们很好(因为数据库事务没有被提交)。如果两者都没有被刷新或都被刷新,那么我们也可以。

据我了解,写入将是不确定的。即,我不知道操作系统会保证在“2”之前写“1”。(我错了吗?)

所以,如果“2”被刷新,但“1”没有被刷新,那么我们就有问题了。

我观察到数据库已正确更新,但文件中有垃圾:最后三分之二的数据是二进制“零”。现在,我不知道当您在蓝屏时刷新文件部分时会是什么样子,但如果它看起来像这样,我不会感到惊讶。

我可以保证这是原因吗?不,我不能保证这一点。我只是在猜测。可能只是由于磁盘故障或蓝屏导致文件“自然”损坏。

关于性能,这是我相信我可以处理的事情。

例如,SQLite 的默认行为是在每次提交事务时执行完整文件刷新(使用 FlushFileBuffers)。他们很清楚,如果您不这样做,那么在系统崩溃时,您的数据库可能已损坏。

另外,我相信我可以通过仅在“检查点”刷新来减轻性能影响。例如,写入 50 个文件,刷新批次,然后写入数据库。

这一切成为问题的可能性有多大?打败我。但是,我的应用程序很可能会在系统故障时或前后存档,因此您可能更可能认为。

希望这能解释为什么我不想这样做。

4

3 回答 3

3

你为什么要这个?操作系统将确保数据在适当的时候刷新到磁盘。如果您访问它,它将从缓存或磁盘返回数据,因此这对您来说是透明的。

如果您在发生灾难时需要一些安全措施,那么您必须调用FlushFileBuffers,例如通过在运行外部进程后创建一个具有管理员权限的进程。但这会严重影响整台机器的性能。

您唯一的其他选择是修改其他进程的源。

[编辑] 最简单的解决方案可能是在您的进程中复制文件,然后刷新副本(因为您有句柄)。以“未在数据库中提交”的名称保存副本。

然后更新数据库。写入数据库,“从文件更新......”。如果下次该条目已经存在,则不要更新数据库,跳过此步骤。

将数据库刷新到磁盘。

将文件重命名为“文件已处理到数据库”。重命名是一个原子操作(所以它要么发生,要么不发生)。

如果您想不出适合不同状态的文件名,请使用子文件夹并在它们之间移动文件。

于 2009-11-29T16:18:08.573 回答
2

好吧,这里没有有吸引力的选择。没有记录的方法可以从流程中检索您需要的文件句柄。虽然有未记录的,但只有仔细考虑才能去那里(通过 DuplicateHandle)。

是的,在卷句柄上调用 FlushFileBuffers 是记录的方式。您可以通过让服务进行调用来避免特权问题。使用标准进程互操作机制之一从您的应用程序中与之交谈。名称以 Global\ 为前缀的命名管道可能是最简单的方法。

于 2009-11-29T18:00:23.070 回答
0

在您更新后,我认为http://sqlite.org/atomiccommit.html可以为您提供所需的答案。

SQLite 确保所有内容都刷新到磁盘的方式有效。所以它也适合你——看看源代码。

于 2009-11-30T10:36:34.707 回答