Winapi 如果重置电源,如何保护带有Win32 API的文件不被损坏? 在C++ Win32应用程序中,我用这样的代码附加一个64K的块来写一个大文件: auto h = ::CreateFile( "uncommited.dat", FILE_APPEND_DATA, // open for writing FILE_SHARE_READ, // share for reading NULL, // default security CREATE_NEW, // create new file only FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template for (int i = 0; i < 10000; ++i) { ::WriteFile(h, 64K);}

Winapi 如果重置电源,如何保护带有Win32 API的文件不被损坏? 在C++ Win32应用程序中,我用这样的代码附加一个64K的块来写一个大文件: auto h = ::CreateFile( "uncommited.dat", FILE_APPEND_DATA, // open for writing FILE_SHARE_READ, // share for reading NULL, // default security CREATE_NEW, // create new file only FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template for (int i = 0; i < 10000; ++i) { ::WriteFile(h, 64K);},winapi,Winapi,假设MoveFile是某种原子操作,当应用程序重新启动时,打开“committed.dat”作为有效,删除“uncommitted.dat”作为损坏。或者有更好的方法吗?MoveFile可以在正确的情况下正常工作。但它也有一些问题——例如,不能使用新名称保存现有文件 如果可能发生这种情况(您基本上是在更新一个现有文件,以确保它不会因为制作副本、修改副本、然后用新文件替换旧文件而损坏),而不是您可能想要使用的MoveFile 使用ReplaceFile,您可以将数据写入uncommitted.da

假设MoveFile是某种原子操作,当应用程序重新启动时,打开“committed.dat”作为有效,删除“uncommitted.dat”作为损坏。或者有更好的方法吗?

MoveFile
可以在正确的情况下正常工作。但它也有一些问题——例如,不能使用新名称保存现有文件

如果可能发生这种情况(您基本上是在更新一个现有文件,以确保它不会因为制作副本、修改副本、然后用新文件替换旧文件而损坏),而不是您可能想要使用的
MoveFile

使用
ReplaceFile
,您可以将数据写入uncommitted.dat(或您喜欢的任何名称)。然后是的,您可能需要执行
FlushFileBuffers
,最后执行
ReplaceFile
以将旧文件替换为新文件。这利用了NTFS日志记录(它适用于文件系统元数据,而不是文件的内容),确保只有两种可能发生的情况之一:要么是旧文件(完全完整),要么是新文件(也完全完整)。如果电源在进行更改时死亡,NTFS将使用其日志来回滚事务。


NTFS也支持事务,但Microsoft通常不建议应用程序直接使用它。显然,自从他们在Windows Vista中添加它以来,它并没有得到太多的使用,MSDN暗示它可能会在未来的Windows版本中被删除。

对于仅附加的场景,您可以将数据拆分为块(恒定或可变大小)。每个块都应该附带某种形式的校验和(SHA、MD5、CRC)

崩溃后,您可以按顺序读取每个块并验证其校验和。第一个损坏的块和随后的所有块都应视为丢失(最终您可以检查它们并手动恢复)

若要附加更多数据,请将文件截断到最后一个正确块的末尾


您可以并行写入两个副本,然后在崩溃后选择一个具有更多良好块的副本。

解决方案是投资于一个新的副本。您无法保护软件免受硬件故障的影响。您提出的备选方案也面临着同样的问题,即在调用
MoveFile
过程中断电。MoveFile不是原子操作吗?NTFS不保证如果有“committed.dat”则它是有效的吗?我的意思是,该文件可以是“committed.dat”或“uncommitted.dat”,但不能是“unXQERQR.XXx”。它在规范层可能是原子的,但在深层,当数据到达磁盘时,您无法采取任何措施来防止断电。当磁铁在写入8位中的7位后失去电流时,它该怎么办?您可以在文件末尾写入一个字符串,以指示复制成功完成,再加上每隔几分钟自动保存一次……IInspectable,您的意思是,即使在例如,服务器上是否使用了特定的高质量SSD磁盘?在这种情况下,数据库事务如何工作?如果我将数据写入“uncommitted.dat”,然后执行FlushFileBuffers和MoveFile(“uncommitted.dat”、“committed.dat”)。如果应用程序在断电后重新启动时存在“committed.dat”,那么它是否未损坏?或者至少NTFS规范保证了这一点?在这种情况下NTFS是否使用日志记录?如果我丢失了文件,这是一个问题。我只需要让应用程序在断电后正确恢复。这个文件是某种数据缓存,类似于持久队列。如果重启后我既没有“uncommitter.dat”也没有“committed.dat”,我的应用程序将继续处理接收到的新数据,并丢失缓存数据。我正在考虑此选项。也许这是最好的。但是对于一个块,我需要两个校验和-第一个校验和用于块大小(即8字节整数),第二个校验和用于块本身,因为块的大小是可变的。
FlushFileBuffers(h);
MoveFile("uncommited.dat", "commited.dat");