调用::ReplaceFile时能否持有被替换文件的有效打开句柄?
问题背景
我希望调用::ReplaceFile的同时,为本进程持有被替换文件的锁,避免替换过程中其他进程读取该文件。而持有锁要求文件通过::CreateFile打开,且在锁有效期内保持打开状态。
如果进程关闭了存在未解除锁的文件,操作系统会自动解锁这些锁。
但只要我持有打开的句柄,::ReplaceFile就会调用失败,测试代码如下:
#include <iostream> #include "Windows.h" int main() { HANDLE fileHandle = ::CreateFile( L"Hello.txt", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, 0, 0); if (!::ReplaceFile(L"World.txt", L"Hello.txt", NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0)) { const DWORD err(::GetLastError()); std::cout << "Uh oh! " << err << "\n"; } ::CloseHandle(fileHandle); }
运行输出:
Uh oh! 32
错误码32对应ERROR_SHARING_VIOLATION,删除::CreateFile调用后替换操作即可成功。
核心疑问
有没有办法在持有替换相关文件打开句柄的同时完成操作?如果没有,是否有具备原子性的::ReplaceFile替代方案?
解答
冲突原因
::ReplaceFile内部会尝试以独占访问方式打开被替换的目标文件,你持有该文件的打开句柄时,即便你设置了所有共享权限,该API的内部打开逻辑依然会触发共享冲突,这是Windows文件系统的底层设计行为,没有办法在保持目标/源文件句柄打开的同时成功调用::ReplaceFile。
可行的原子替换方案
你可以通过以下方案实现带访问防护的原子文件替换:
- 调整锁持有时机:先打开目标文件加锁,阻止其他进程访问,提前准备好替换用的新文件内容,之后解锁并关闭目标文件句柄,立刻调用
::ReplaceFile完成替换,替换完成后重新打开目标文件加锁即可。该流程仅有替换瞬间的极短窗口无锁保护,几乎不会出现竞争问题。 - 用命名互斥量替代文件锁:如果可以接受不用文件级锁,你可以创建跨进程命名互斥量作为文件的访问守卫,所有进程读写该文件前都要先持有互斥量,替换操作全程持有互斥量即可阻止其他进程访问,不需要持有目标文件句柄,完全不影响
::ReplaceFile的调用,原子性和原方案一致。 - 自定义原子替换逻辑:你可以自己实现和
::ReplaceFile原子性等价的替换逻辑:先将新文件写入到同目录下的临时文件,再通过::MoveFileEx传入MOVEFILE_REPLACE_EXISTING参数完成替换,该调用本身具备和::ReplaceFile完全相同的原子性保障,配合跨进程互斥量即可实现安全的原子替换。
内容的提问来源于stack exchange,提问作者Brotcrunsher
相关产品推荐
相关产品推荐

