在POSIX系统上,rename(2)提供了一个原子重命名操作,包括覆盖目标文件(如果存在并且权限允许)。
有什么方法可以在Windows上获得相同的语义吗?我知道Vista和Server 2008上的MoveFileTransacted(),但我需要它来支持Win2k及更高版本。
这里的关键词是原子。。。解决方案不能以任何方式失败,从而使操作处于不一致状态。
我看到很多人说这在win32上是不可能的,但我问你,真的吗?
请尽可能提供可靠的引文。
请参阅Win32中的ReplaceFile()
(https://www.microsoft.com/en-us/research/wp-content/uploads/2006/04/tr-2006-45.pdf)
Win32不保证原子文件元数据操作。我会提供一个引文,但没有——没有书面或文件证明的保证这一事实意义重大。
你必须编写自己的例程来支持这一点。这很不幸,但你不能指望win32能提供这种级别的服务——它根本不是为它设计的
在Windows Vista和Windows Server 2008中添加了一个原子移动功能-MoveFileTransacted()
不幸的是,这对旧版本的Windows没有帮助。
MSDN上有一篇有趣的文章。
从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_TROUGH选项。后者在MSDN中有这样的描述:
设置此值可确保作为复制和删除执行的移动操作在函数返回。冲洗发生在复制操作结束时。
我知道这不一定与重命名操作相同,但我认为这可能是你能得到的最好保证——如果它这样做是为了文件移动,那么它应该是为了更简单的重命名。
MSDN文档避免明确说明哪些API是原子的,哪些不是,但Niall Douglas在其Cppcon 2015演讲中指出,唯一的原子函数是
按句柄设置文件信息
其中CCD_ 6被设置为真。它从Windows Vista/2008 Server开始提供。
Niall是一个高度复杂的LLFIO库的作者,也是文件系统竞争条件方面的专家,所以我相信,如果你正在编写一个原子性至关重要的算法,最好是安全的,而不是抱歉的,并使用建议的函数,即使ReplaceFile
的描述中没有任何内容表明它不是原子的。
有相当多的答案,但不是我所期望的。。。我的理解(也许是错误的)是,MoveFile可以是原子的,前提是正确的恒星对齐,使用标志,并且文件系统在源和目标上相同。否则,操作将回退到[复制->删除]文件。
鉴于此;我还知道MoveFile——当它是原子文件时——只是设置文件信息,也可以在这里完成:setfileinfobyhandle。
有人做了一个名为"文件系统竞赛"的演讲,对这一点进行了更深入的探讨。(大约三分之二的人在谈论原子更名)
有std::rename,并以C++17 std::filesystem::rename开头。未指定如果目的地存在std::rename
:会发生什么
如果存在new_filename,则行为由实现定义。
POSIX重命名是原子替换现有文件所必需的:
此rename()函数对于常规文件等效于定义的通过ISO C标准。它在这里的包含将该定义扩展到包括对目录的操作,并指定当参数命名已存在的文件。该规范要求函数的操作是原子的。
值得庆幸的是,std::filesystem::rename
要求它的行为与POSIX:一样
将old_p标识的文件系统对象移动或重命名为如果通过POSIX重命名
然而,当我尝试调试时,VS2019(截至2020年3月)实现的std::filesystem::rename
似乎只是调用MoveFileEx,在某些情况下它不是原子的。因此,当它实现中的所有错误都得到修复时,我们可能会看到可移植的原子std::filesystem::rename
。