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