Windows上是否可以重命名原子文件(带覆盖)?

时间:2008-10-03 15:25:25

标签: windows winapi posix

在POSIX系统上,rename(2)提供原子重命名操作,包括覆盖目标文件(如果存在)以及权限是否允许。

有没有办法在Windows上获得相同的语义?我在Vista和Server 2008上了解MoveFileTransacted(),但我需要它来支持Win2k及以上版本。

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

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

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

8 个答案:

答案 0 :(得分:33)

请参阅Win32中的ReplaceFile()http://research.microsoft.com/pubs/64525/tr-2006-45.pdf

答案 1 :(得分:16)

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

你将不得不编写自己的例程来支持这一点。这很不幸,但你不能指望win32提供这种服务水平 - 它根本就不是为它而设计的。

答案 2 :(得分:15)

在Windows Vista和Windows Server 2008中添加了原子移动功能 - MoveFileTransacted()

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

Interesting article here on MSDN

答案 3 :(得分:9)

你仍然在Windows上进行了rename()调用,不过我想象如果你不知道你正在使用的文件系统就无法做出你想要的保证 - 例如,如果你正在使用FAT,则无法保证。

但是,您可以使用MoveFileEx并使用MOVEFILE_REPLACE_EXISTING  和MOVEFILE_WRITE_THROUGH选项。后者在MSDN中有这样的描述:

  

设置此值可确保a   移动作为复制和删除执行   操作之前已刷新到磁盘   函数返回。发生冲洗   在复制操作结束时。

我知道这不一定与重命名操作相同,但我认为它可能是你得到的最好保证 - 如果它是为文件移动而做的,它应该用于更简单的重命名。

答案 4 :(得分:2)

相当多的答案,但不是我期待的答案......我有理解(可能是错误的)MoveFile 可能原子,前提是正确的星星对齐,使用了旗帜,以及文件系统在源上与目标相同。否则,操作将回退到[Copy-> Delete]文件。

鉴于此;我也理解MoveFile - 当它是原子的时 - 只是设置文件信息,也可以在这里完成:setfileinfobyhandle

有人发表了一个名为" Racing the Filesystem"关于这一点进一步深入。 (他们谈论原子重命名大约2 / 3rds)

答案 5 :(得分:2)

从Windows 10 1607开始,NTFS确实支持原子取代重命名操作。为此,请调用NtSetInformationFile(...,FileRenameInformationEx,...)并指定FILE_RENAME_POSIX_SEMANTICS标志。或者等效地在Win32中调用SetFileInformationByHandle(...,FileRenameInfoEx,...)并指定FILE_RENAME_FLAG_POSIX_SEMANTICS标志。

答案 6 :(得分:2)

MSDN文档避免清楚地说明哪些API是原子的,哪些不是原子的,但是Niall Douglas在他的Cppcon 2015 talk中指出,唯一的原子函数是

SetFileInformationByHandle

,其中FILE_RENAME_INFO.ReplaceIfExists设置为true。从Windows Vista / 2008 Server开始可用。

Niall是一个高度复杂的LLFIO library的作者,并且是文件系统争用条件的专家,所以我相信,如果您正在编写原子性至关重要的算法,最好不要担心,并使用建议的功能即使ReplaceFile的描述中没有任何内容表明它不是原子的。

答案 7 :(得分:0)

std::rename,以C++17 std::filesystem::rename开头。 尚未确定如果目的地与std::rename存在会发生什么情况:

如果存在new_filename,则行为是实现定义的。

POSIX renamerequired to replace existing files atomically

此rename()函数对于常规文件等效于所定义的 通过ISO C标准。它的包含将此处的定义扩展为 包括对目录的操作并指定新的行为 参数命名一个已经存在的文件。该规范 要求该函数的动作必须是原子的。

感谢std::filesystem::rename要求它的行为就像POSIX:

将由old_p标识的文件系统对象移动或重命名为new_p为 如果通过POSIX重命名

但是,当我尝试调试时,看来std::filesystem::rename是由VS2019实现的(截至2020年3月)只是调用MoveFileEx,在某些情况下并不是原子的。 因此,可能在修复了其实现中的所有错误之后,我们将看到可移植的原子std::filesystem::rename