文件内容损坏的原因

8
我在一款应用程序中遇到了一个反复出现的问题。它每隔30分钟左右就会产生一个相当简单的XML文件。这些数据文件通常很小 - 例如小于5KB。它不会锁定文件,而是每次都从头重新创建它。我很幸运地在测试机器上看到了这个问题,我观察到文件被损坏并设置为“nulls”(即十六进制中的00)。真正奇怪的是,它与应该有的长度完全相同。我试图在保存过程中非常仔细地操作:1.将xml写入与我要实际保存它的相同目录的临时文件中;2.使用MOVEFILE_WRITE_THROUGH设置执行Win32 MoveFile()(所以它应该阻塞直到移动真正完成),将文件移动以替换现有的数据文件;甚至我还锁定了一个Mutex来确保这不是线程问题。这种情况并不经常发生,可能只有1000个用户中的1个。我曾经观察到过在写入过程中发生电源故障或BSOD导致数据文件损坏的情况,例如,一个文件的32kb全部为空值。但是看起来它正在比我预期的更频繁地发生,考虑到在写入期间发生电源故障的几率以及我正在使用MOVEFILE_WRITE_THROUGH。你有什么想法吗? 约翰
一些问题的答案:
  • Q: 为什么不直接写入文件?

  • A: 避免这种情况是为了使软件更少受到电源故障问题的影响。例如,当您正在写文件时发生崩溃/断电/蓝屏,那么您肯定会有一个损坏的文件。编写临时文件,然后移动它是一种常用且简单的方式,可以尽可能地确保原子文件操作(好吧,在不使用 NTFS 特定 API 的情况下尽可能接近)。我应该说,该软件是一个存档/备份系统,因此我必须比其他应用程序更加小心数据一致性。

  • Q: 这种情况是否在正常操作中发生?

  • A: 由于这个问题在实际情况中发生,我只有一些线索,所以我不能确定。我可以说,该软件在99.9%的时间内可靠工作。我想这就是我的问题所在:这只是因为 BSOD/电源故障引起的随机不幸还是一个错误?

  • Q: 哪个环境/操作系统:

  • A: XP、Vista、7、Server 200X。最可能是 NTFS,但也可能是 FAT32。

  • Q: 在移动文件之前我是否关闭了文件?

  • A: 是的。我正在使用 C++ 流,并在 MoveFile 之前调用 close()。

  • Q: 其他哪些进程正在访问该文件?

  • A: 没有由我管理的进程。显然,我无法控制病毒检查器、文件夹同步器等。该文件位于用户计算机的 AppData\Local 文件夹中。


1
为什么要费这么大的劲呢?真的值得吗?为什么不直接写入文件,避免所有的互斥、移动等操作呢? - David Heffernan
这种情况只会在写入期间或之后不久出现断电或崩溃时发生吗?还是在正常操作期间也会发生? - bdonlan
1
另外,你要针对哪些版本的Windows进行开发? - bdonlan
其他进程正在访问该文件,以及如何访问? - Alan Stokes
非常好的问题 - 谢谢。我的答案已附加到上面的问题正文中。 - John
显示剩余2条评论
3个回答

4
根据我的经验,可能是由于Windows中的文件缓存引起的。您应该尝试使用带有FILE_FLAG_WRITE_THROUGH参数的CreateFile()来保存文件。通过这种方式保存文件可以确保文件落地到硬盘中。
我编写了一个小程序来测试这个问题。如果程序使用std::ofstream创建文件,并使用MoveFileEx()和MOVEFILE_WRITE_THROUGH移动文件,如果在文件移动完成后立即关闭虚拟机电源(而不是正常关机),文件几乎每次都会损坏。否则,如果程序使用CreateFile()和FILE_FLAG_WRITE_THROUGH来创建文件,然后再次执行相同的操作,文件不会损坏(我测试了大约10次,没有发生过)。
经过这些简单的测试,我认为您应该尝试使用CreateFile()和FILE_FLAG_WRITE_THROUGH来解决您的问题。

更多信息:
文件缓存(Windows)
Windows Internals第6版,第11章 缓存管理器


0

以下是一些建议:

  • 在关键信息写入后或长时间不写入前刷新流。
  • 确认没有其他实体正在写入该文件。
  • 确认缓冲的数据没有被其他代码覆盖。
  • 在长时间不写入期间关闭文件。

我不会保持文件处于打开状态。每次我都是从头开始写入文件。 - John

-1

我遇到了相同的问题,我的代码和你解释的一模一样,这似乎很不正常,但是为了让它正常工作,我制作了多个备份文件,当读取时发生问题时,我假定它已经损坏,并从备份文件中读取。


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