作者:拾味湖 | 2023-09-04 14:58
在POSIX系统上,rename(2)提供原子重命名操作,包括覆盖目标文件(如果存在)以及权限是否允许.
有没有办法在Windows上获得相同的语义?我知道Vista和Server 2008上的MoveFileTransacted(),但我需要它来支持Win2k及更高版本.
这里的关键词是原子 ......解决方案不能以任何方式使操作失败,使操作处于不一致状态.
我见过很多人说在win32上这是不可能的,但我问你,是不是真的?
如果可能,请提供可靠的引用.
1> 小智..:
请参阅ReplaceFile()
Win32(http://research.microsoft.com/pubs/64525/tr-2006-45.pdf)
微软实习生在这里.我有这个问题,所以我问了一个从事过NTFS的人.移动数据的部分是原子的,因此虽然可以在修改文件属性时中断,但数据本身移动的部分是原子的.
该MS研究论文的相关段落:"在UNIX下,rename()保证原子地覆盖旧版本的文件.在Windows下,ReplaceFile()调用用于原子地将一个文件替换为另一个文件."
http://msdn.microsoft.com/en-us/library/windows/desktop/hh802690%28v=vs.85%29.aspx表示ReplaceFile可以原子使用:"许多处理"类文档"数据的应用程序倾向于将整个文档加载到内存中,对其进行操作,然后将其写回以保存更改.这里所需的原子性是更改完全应用或根本不应用,因为不一致的状态会导致文件损坏一种常见的方法是将文档写入新文件,然后用新文件替换原始文件.一种方法是使用ReplaceFile API."
如果您阅读http://msdn.microsoft.com/en-us/library/aa365512(VS.85).aspx,您会看到`ReplaceFile`是一个复杂的合并操作,没有任何迹象表明它是原子的.
请特别注意为ReplaceFile记录的各种返回代码,它们都对应于操作的不同程度的部分(即非原子)完成.
值得注意的是,与`rename()`,`ReplaceFile()`*如果新文件不存在会失败*-因此您还有另一个原子性问题-检查是否存在,然后在单个原子步骤中进行替换。 ..叹气...
2> Adam Davis..:
Win32不保证原子文件元数据操作.我会提供一个引用,但没有 - 这个事实是没有书面或文件保证意味着同样多.
你将不得不编写自己的例程来支持这一点.这是不幸的,但你不能指望win32提供这种服务水平 - 它根本就不是为它设计的.
@AdamDavis仍然是一个耻辱.原子覆盖是一个至关重要的特性.在文件系统上,这是了解您拥有旧版本或新命名blob的唯一方法.
我觉得很难相信.这意味着即使我们正在处理像NTFS这样的可靠系统,停电也很容易破坏文件系统.
这是有道理的,但它也非常可怕.最终得到一个既不是原始文件名也不是最终名称的文件名,几乎就是灾难的秘诀.特别是因为(iirc)POSIX标准已经需要原子元文件操作.
@mafutrct请记住,问题不在于破坏文件系统 - 而在于确保重命名成功完成,或者根本不会发生.文件系统不会被破坏,但文件名可能不会保留原始状态或最终状态.NTFS是一个日志文件系统,因此它不会(容易)被破坏,但是根据文件重命名的复杂性或操作顺序,它可能不会保留在原始或期望的最终状态.
3> 小智..:
在Windows Vista和Windows Server 2008中添加了原子移动功能 - MoveFileTransacted()
不幸的是,这对旧版本的Windows没有帮助.
这篇有趣的文章在MSDN上.
隐藏在评论中:此**不适用于网络共享**.
4> gbjbaanb..:
你仍然在Windows上进行rename()调用,虽然我想你不知道你正在使用的文件系统就无法做出你想要的保证 - 如果你使用FAT就不能保证.
但是,您可以使用MoveFileEx并使用MOVEFILE_REPLACE_EXISTING和MOVEFILE_WRITE_THROUGH选项.后者在MSDN中有这样的描述:
设置此值可确保在函数返回之前将作为复制和删除操作执行的移动刷新到磁盘.刷新发生在复制操作结束时.
我知道这不一定与重命名操作相同,但我认为这可能是你得到的最好的保证 - 如果它为文件移动做的那样,它应该用于更简单的重命名.
据我所知,如果目标存在并且在数据复制步骤期间发生I/O错误,则此"原始"目标将丢失,因此MoveFileEx不符合您的要求.这就是后来添加MoveFileTransacted的原因.
MoveFileEx应该不错.它有一个名为MOVEFILE_COPY_ALLOWED的标志,表示:"如果要将文件移动到另一个卷,该函数将使用CopyFile和DeleteFile函数模拟移动." 所以只是不要传递这个标志,你应该有一些等同于POSIX重命名的东西,是吗?
5> 小智..:
从Windows 10 1607开始,NTFS确实支持原子取代重命名操作。为此,请调用NtSetInformationFile(...,FileRenameInformationEx,...)并指定FILE_RENAME_POSIX_SEMANTICS标志。或等效地在Win32中调用SetFileInformationByHandle(...,FileRenameInfoEx,...)并指定FILE_RENAME_FLAG_POSIX_SEMANTICS标志。