在 Windows 上可以重命名原子文件吗?

在 POSIX 系统上,重命名(2)提供了原子重命名操作,包括在目标文件存在并且权限允许的情况下覆盖目标文件。

有没有办法在 Windows 上获得相同的语义?我知道 Vista 和 Server2008上的 MoveFileTransact () ,但是我需要它来支持 Win2k 和更高版本。

这里的关键词是 原子弹... 解决方案不能以任何方式失败,使操作处于不一致的状态。

我见过很多人说在 win32上这是不可能的,但我问你,真的是这样吗?

如果可能,请提供可靠的引证。

30220 次浏览

在 Windows 上仍然可以使用 rename ()调用,不过我想,如果不知道所使用的文件系统,就无法做出您想要的保证——例如,如果您使用的是 FAT,就无法保证。

但是,您可以使用 MoveFileEx 并使用 MOVEFILE _ REPLACE _ EXISTING 和 MOVEFILE _ WRITE _ THROUGH 选项:

设置此值可以保证 作为拷贝执行的移动和删除 之前将操作刷新到磁盘 函数返回。刷新发生 在复制操作结束时。

我知道这不一定和重命名操作相同,但我认为这可能是最好的保证,如果它为文件移动这样做,它应该为一个更简单的重命名。

Win32不保证原子文件元数据操作。我想提供一个引证,但没有-事实上,没有书面或文件的保证意味着这么多。

你必须编写自己的程序来支持这一点。这是不幸的,但你不能指望 win32提供这种水平的服务-它只是不是为它而设计的。

在 WindowsVista 和 WindowsServer2008中添加了一个原子移动函数-MoveFileTransact ()

不幸的是,这对旧版本的 Windows 没有帮助。

MSDN 上有趣的文章。

有相当多的答案,但不是我所期望的... ... 我理解(也许是错误的) MoveFile 可能是原子提供了正确的星星对齐,标志被使用,文件系统在源和目标上是相同的。否则,操作将回退到[ Copy-> Delete ]文件。

考虑到这一点,我还了解到 MoveFile ——当它是原子的时候——只是设置文件信息,这也可以在这里完成: Setfileinfoby 句柄

有人做了一个名为“ 与文件系统赛跑”的演讲,对此进行了更深入的探讨。(约三分之二下来,他们谈论原子重命名)

从 Windows101607开始,NTFS 确实支持原子替换重命名操作。调用 NtSetInformationFile(..., FileRenameInformationEx, ...)并指定 FILE_RENAME_POSIX_SEMANTICS标志。

或者等效地在 Win32中调用 SetFileInformationByHandle(..., FileRenameInfoEx, ...)并指定 FILE_RENAME_FLAG_POSIX_SEMANTICS标志。

MSDN 文档避免清楚地说明哪些 API 是原子的,哪些不是原子的,但是 Niall Douglas 在他的 2015年全球化学品论坛简介中指出,唯一的原子函数是

SetFileInformationByHandle

FILE_RENAME_INFO.ReplaceIfExists设置为 true。可以从 WindowsVista/2008服务器开始使用。

Niall 是一个高度复杂的 LLFIO 库的作者,并且是文件系统竞争条件的专家,所以我相信如果你正在编写一个原子性至关重要的算法,最好是安全的,而不是抱歉,并使用建议的函数,即使在 ReplaceFile的描述中没有说明它不是原子性的。

重命名和开始的 C + + 17 文件系统: : 重命名。 如果目的地与 std::rename一起存在,会发生什么情况还没有说明:

如果 new _ filename 存在,则行为是实现定义的。

然而,POSIX 重命名需要自动替换现有文件:

这个 rename ()函数与定义的常规文件等效 这里包含的定义将这个定义扩展到 包含对目录的操作,并指定新的 参数命名一个已经存在的文件 要求函数的作用是原子的。

值得庆幸的是,std::filesystem::rename要求它的行为与 POSIX 类似:

将 old _ p 标识的文件系统对象移动或重命名为 new _ p 如果通过 POSIX 重命名

然而,当我尝试调试时,VS2019实现的 std::filesystem::rename(截至2020年3月)似乎只是调用 移动文件快递,在某些情况下它不是原子的。 因此,当修复了其实现中的所有 bug 时,我们可能会看到可移植的原子 std::filesystem::rename