在 POSIX 系统上 rename(2) 提供原子重命名操作,包括在目标文件存在且权限允许的情况下覆盖目标文件。
有没有办法在 Windows 上获得相同的语义?我知道 Vista 和 Server 2008 上的 MoveFileTransacted(),但我需要它来支持 Win2k 及更高版本。
这里的关键词是原子的……解决方案不能以任何方式失败,从而使操作处于不一致的状态。
我看到很多人说这在win32上是不可能的,但我问你,真的吗?
如果可能,请提供可靠的引用。
参见ReplaceFile()
Win32 ( http://research.microsoft.com/pubs/64525/tr-2006-45.pdf )
Win32 不保证原子文件元数据操作。我会提供引文,但没有——没有书面或书面保证的事实同样重要。
您将不得不编写自己的例程来支持这一点。很不幸,但你不能指望 win32 提供这种级别的服务——它根本不是为它设计的。
在 Windows Vista 和 Windows Server 2008 中添加了原子移动函数 - MoveFileTransacted()
不幸的是,这对旧版本的 Windows 没有帮助。
从 Windows 10 1607 开始,NTFS 确实支持原子取代重命名操作。要执行此调用NtSetInformationFile(..., FileRenameInformationEx, ...)
并指定FILE_RENAME_POSIX_SEMANTICS
标志。
或者等效地在 Win32 调用中SetFileInformationByHandle(..., FileRenameInfoEx, ...)
指定FILE_RENAME_FLAG_POSIX_SEMANTICS
标志。
您仍然可以在 Windows 上调用 rename() ,尽管我想在不知道您正在使用的文件系统的情况下无法做出您想要的保证 - 例如,如果您使用的是 FAT,则无法保证。
但是,您可以使用 MoveFileEx 并使用 MOVEFILE_REPLACE_EXISTING 和 MOVEFILE_WRITE_THROUGH 选项。后者在 MSDN 中有这样的描述:
设置此值可确保在函数返回之前将作为复制和删除操作执行的移动刷新到磁盘。刷新发生在复制操作结束时。
我知道这不一定与重命名操作相同,但我认为这可能是你能得到的最好保证——如果它是为了文件移动,它应该是为了更简单的重命名。
MSDN 文档避免明确说明哪些 API 是原子的,哪些不是,但 Niall Douglas 在他的Cppcon 2015 演讲中指出,唯一的原子函数是
设置FILE_RENAME_INFO.ReplaceIfExists
为真。它从 Windows Vista / 2008 Server 开始可用。
Niall 是一个高度复杂的LLFIO 库的作者,并且是文件系统竞争条件方面的专家,所以我相信如果你正在编写一个原子性至关重要的算法,最好是安全而不是抱歉,并使用建议的函数,即使ReplaceFile
's中没有任何内容描述表明它不是原子的。
相当多的答案,但不是我所期望的......我有这样的理解(也许是错误的),只要正确的星对齐,使用标志,并且源文件系统与目标文件系统相同,MoveFile可以是原子的. 否则,操作将退回到[复制->删除]文件。
鉴于; 我也了解到 MoveFile - 当它是原子的时 - 只是设置文件信息,这也可以在这里完成:setfileinfobyhandle。
有人做了一个名为“ Racing the Filesystem ”的演讲,对此进行了更深入的讨论。(大约 2/3 下他们谈论原子重命名)
有std::rename并从C++17 std::filesystem::rename开始。如果destination存在于std::rename
:
如果 new_filename 存在,则行为是实现定义的。
但是,POSIX rename需要以原子方式替换现有文件:
对于常规文件,此 rename() 函数等效于 ISO C 标准定义的文件。它在这里的包含扩展了该定义以包括对目录的操作,并指定新参数命名已存在的文件时的行为。该规范要求函数的操作是原子的。
值得庆幸的是,std::filesystem::rename
它要求它的行为就像 POSIX:
将由 old_p 标识的文件系统对象移动或重命名为 new_p,就像通过 POSIX 重命名一样
但是,当我尝试调试时,似乎std::filesystem::rename
VS2019(截至 2020 年 3 月)实现的只是调用MoveFileEx,在某些情况下这不是原子的。因此,可能,当其实现中的所有错误都得到修复时,我们将看到可移植的 atomic std::filesystem::rename
。