在POSIX系统上,rename(2)提供了原子重命名操作,包括覆盖目标文件(如果存在)以及是否允许权限。
有没有办法在Windows上获得相同的语义?我知道Vista和Server 2008上的MoveFileTransacted(),但是我需要它来支持Win2k及更高版本。
这里的关键词是原子的……解决方案一定不能以使操作处于不一致状态的任何方式失败。
我见过很多人说这在win32上是不可能的,但是我问你,真的吗?
如果可能,请提供可靠的引用。
在POSIX系统上,rename(2)提供了原子重命名操作,包括覆盖目标文件(如果存在)以及是否允许权限。
有没有办法在Windows上获得相同的语义?我知道Vista和Server 2008上的MoveFileTransacted(),但是我需要它来支持Win2k及更高版本。
这里的关键词是原子的……解决方案一定不能以使操作处于不一致状态的任何方式失败。
我见过很多人说这在win32上是不可能的,但是我问你,真的吗?
如果可能,请提供可靠的引用。
Answers:
Win32不保证原子文件元数据操作。我会提供引用,但没有引用-没有书面或书面担保的事实就足够了。
您将必须编写自己的例程来支持此操作。不幸的是,但是您不能指望win32提供这种级别的服务-根本不是为它设计的。
LOGFILE
并且LOGBACKUP
要定期将日志文件移至备份并启动新的日志文件,则可以将日志文件重命名为logbackup。操作系统必须先删除日志备份,然后重命名日志文件-可能会发生删除,但不会重命名,然后丢失两个日志文件,这在软件中解决起来并不是一个小问题。
参见ReplaceFile()
Win32(http://research.microsoft.com/pubs/64525/tr-2006-45.pdf)
ReplaceFile
是一个复杂的合并操作,没有迹象表明它是原子的。
从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
设置为true。从Windows Vista / 2008 Server开始可用。
Niall是一个高度复杂的LLFIO库的作者,并且是文件系统争用条件方面的专家,所以我相信,如果您正在编写原子性至关重要的算法,那么最好不要担心,并且即使在ReplaceFile
s中没有任何内容的情况下也使用建议的功能描述指出它不是原子的。
相当多的答案,但不是我期望的。。。我了解(也许不正确)MoveFile可以是原子的,前提是适当的星形对齐,使用了标记并且源上的文件系统与目标相同。否则,该操作将退回到[Copy-> Delete] File。
鉴于; 我还了解到MoveFile-当它是原子时-只是设置文件信息,这也可以在这里完成:setfileinfobyhandle。
有人做了一个名为“竞速文件系统”的演讲,对此进行了更深入的介绍。(大约2 / 3rds以下他们谈论原子重命名)
有std :: rename,以C ++ 17 std :: filesystem :: rename开头。尚不明确如果目的地存在std::rename
:
如果new_filename存在,则行为是实现定义的。
但是,需要使用POSIX重命名来原子地替换现有文件:
对于常规文件,此named()函数等效于ISO C标准定义的文件。它的包含将扩展该定义,以包括对目录的操作,并在新参数命名已存在的文件时指定行为。该规范要求函数的动作是原子的。
幸运的是,std::filesystem::rename
要求它的行为类似于POSIX:
将由old_p标识的文件系统对象移动或重命名为new_p,就像通过POSIX重命名一样
但是,当我尝试调试时,似乎std::filesystem::rename
由VS2019实现(截至2020年3月)只是调用MoveFileEx,在某些情况下这不是原子的。因此,可能在修复了其实现中的所有错误之后,我们将看到可移植的atomic std::filesystem::rename
。