c++ - 文件内容损坏的原因

标签 c++ windows winapi file-io corruption

我在使用应用程序时遇到反复出现的问题。

它有一个相当简单的 XML 文件,它时不时地转储出来,大约每 30 分钟一次。

数据文件通常很小——例如< 5KB。

它不会锁定文件 - 它只是每次都从头开始重新创建它。

我很幸运在测试机器上看到了问题,我观察到文件已损坏并设置为“空值”(即十六进制的 00)。真正奇怪的是,与应该的长度相比,它的长度恰好是正确的。

我在保存过程中非常小心:

  1. 我将 xml 写入一个临时文件,该文件位于我要真正保存它的同一目录中
  2. 我执行 Win32 MoveFile() 并设置了 MOVEFILE_WRITE_THROUGH(因此它应该阻塞直到移动真正完成),以移动文件以替换现有数据文件

我什至锁定了一个 Mutex 以确保这不是线程问题。

这种情况并不经常发生,大约每 1000 个用户中就有 1 个。

现在我过去曾观察到数据文件在写入过程中因电源故障或 BSOD 而损坏,并且我看到过诸如文件的 32kb 全部为 NULL 之类的情况。

但它似乎发生的比我预期的要多,考虑到写入过程中可能会出现电源故障,尤其是因为我正在使用 MOVEFILE_WRITE_THROUGH。

有什么想法吗?

约翰


一些问题的答案:

  • 问:为什么不直接写入文件 答:我避免了这一点,以使软件不易受到电源故障问题的影响。例如。你在写入文件的一半时崩溃/电源故障/BSOD 那么你肯定有一个损坏的文件。执行临时文件写入然后移动是一种常用且简单的方法,可确保您尽可能执行原子文件操作(嗯,在不使用 NTFS 特定 API 的情况下尽可能接近)。我应该说该软件是一个归档/备份系统,因此我必须比其他应用程序更加注意数据的一致性。

  • 问:正常运行时会出现这种情况吗?

  • 答:由于这个问题是在野外发生的,我只是在处理一些线索,所以我不确定。我可以说该软件在 99.9% 的时间里都能可靠地工作。我想这就是我的问题的核心:这只是由 BSOD/电源故障引起的随机不幸还是一个错误?

  • 问:什么环境/操作系统:

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

  • 问:我是不是在移动前关闭文件

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

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

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

最佳答案

根据我的经验,这可能是由Windows 中的文件缓存引起的。您应该尝试使用 CreateFile() 并传入 FILE_FLAG_WRITE_THROUGH 来保存文件。通过这种方式保存文件可以确保文件将登陆硬盘。

我写了一个小程序来测试这个。如果程序使用 std::ofstream 创建文件并使用 MoveFileEx()MOVEFILE_WRITE_THROUGH 移动该文件,文件几乎每次都会损坏,如果文件移动完成后立即关闭(不是关闭)虚拟机;否则,如果程序使用 CreateFile()FILE_FLAG_WRITE_THROUGH 创建文件并再次做同样的事情,文件没有损坏(我测试了大约 10 次,但它没有发生)。

经过这些简单的测试,我认为您应该尝试使用 CreateFile()FILE_FLAG_WRITE_THROUGH 来解决您的问题。

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

关于c++ - 文件内容损坏的原因,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6525943/

相关文章:

ruby - pry 开显示像 [äöüßÄÖÜß](utf-8 编码)这样的字符? (可能是特定于 Windows 的问题?)

c - 我如何在 UEFI 驱动程序中设置固件环境变量

c++ - 如何使用 CryptoAPI 和 SignerSign 为带有附加证书的 EXE 签名

c++ - std::string::c_str() 结果的生命周期是多少?

c++ - 在.lib 中使用#ifdef 并在linkin 项目中定义变量

c++ - 检测 SFINAE 的 POD 类型的第一个成员

c# - GetFocus - Win32api 帮助

C++ 重载运算符==

windows - 当我在 Windows 上创建 .gitignore 文件时抛出 "You must type a file name"错误

c - 要传递给 CreateDIBSection 函数的参数