等待文件可写

Posted

技术标签:

【中文标题】等待文件可写【英文标题】:Wait for a file to be writable 【发布时间】:2015-03-02 09:55:17 【问题描述】:

我正在开发一种将数据写入文件的工具。

在某些时候,文件可能被“锁定”并且在其他句柄关闭之前不可写。

我可以循环使用CreateFile API,直到文件可用于写入访问。

但我在循环中使用CreateFile 有两个问题:

硬盘(缓存)一直在运行...?! 我需要再次调用CreateFile 以获得具有不同标志的有效写入句柄...?!

所以我的问题是:

并立即获得有效句柄的最佳解决方案是什么?

是否有任何事件解决方案或任何东西,允许“排队/保留”一次句柄,以便与其他人没有“不受控制”的竞争条件?

【问题讨论】:

硬盘驱动器没有“始终运行”的问题,这是它们的设计目的...... @JonathanPotter 我不同意。如果硬盘保持在同一扇区,则会出现延迟和性能滞后。它也磨损了。如果这些事情是在记忆中处理的,那就另当别论了。 我的评论有点滑稽,我会同意你的。但你不必担心。文件系统缓存在内存中。反复尝试打开文件句柄几乎肯定不会触及磁盘。 @JonathanPotter 实际上,我会在您最初的评论中指出,这方面的主要问题是,如果服务器在没有断电的情况下连续运行 X+ 年,硬盘是否真的会启动. @JonathanPotter 我几乎接受了这个作为答案。 a/“种族”条件呢?假设有 10 个循环时间不同的进程正在尝试打开文件。 “先到先得”在这里适用吗?很难解释,这就是为什么我在这个问题中加入了“队列/保留”这个词。 【参考方案1】:

文件被“锁定”有两个原因:

    一个实际的文件锁阻止写入,也可能阻止读取文件。 在没有共享访问权限的情况下打开文件(意外或自愿)这甚至会阻止您打开句柄。如果您已经看到 CreateFile 失败,则很可能是这种情况,而不是真正的锁定。

在概念上[1]至少有两种方法可以知道没有其他进程在不忙于等待的情况下锁定了文件:

    通过找出谁持有锁并等待进程或线程退出(或者,直接杀死它们......) 通过自己锁定文件

谁持有锁? 找出锁所有者是相当讨厌的,您可以通过 totally undocumented SystemLocksInformation 类与未记录的 NtQuerySystemInformation 函数一起使用(后者是“仅未记录”,但前者是这样很多无证,很难找到任何信息)。返回的结构解释为here,它包含一个拥有线程ID。

幸运的是,拿着锁假定拿着把手。关闭文件句柄将解锁所有文件范围。这意味着:没有把手就没有锁。

换句话说,问题也可以表示为“谁在持有文件的打开句柄?”。当然,并不是所有持有文件句柄的进程都会锁定文件,但没有进程拥有句柄保证没有进程锁定文件。 找出哪些进程打开了文件的代码要容易得多(使用重启管理器),在 Raymond Chen 的网站上是 readily available。

既然您知道哪些进程和线程持有文件句柄和锁,请列出所有线程/进程句柄并在进程句柄列表中使用WaitForMultipleObjects。当进程退出时,所有句柄都将关闭。 这也透明地处理了“锁定”的可能性,因为进程不共享访问权限。

自己锁定文件 您可以使用异步操作的LockFileEx。请注意,LockFileEx 需要一个有效的句柄,该句柄以 either 读取或写入权限打开(可能无法获得写入权限,但读取应该几乎总是有效 - 即使您实际上被阻止读取独占锁,如果没有锁,仍然可以创建一个可以读取的句柄。

然后,您可以通过OVERLAPPED 结构中的事件或在完成端口上等待异步锁定完成,同时还可以做其他有用的事情。锁定文件后,您就知道没有其他人将其锁定。


[1] 措辞“概念上” 表明我很确定这两种方法都行得通,但我还没有测试过它们。

【讨论】:

只是为了澄清你答案的最后一部分:你建议我CreateFileA()GENERIC_READ 不共享然后尝试锁定它以进行写作?我需要的正是 OP 想要的:等待文件被解锁,然后锁定它并用它做一些事情,包括写入。我想等待它,而不是轮询状态。我理解正确吗? @TomášZato: CreateFile 仅不共享访问权限显然 锁定文件,因为 CreateFile 会因其他人尝试而失败,这不完全一样!它不会使已经握住手柄的其他人无效。此外,如果不能选择失败,那么在没有循环的情况下,你真的不能CreateFile(据我所知),因为它没有“阻塞直到成功”的操作模式。一旦你有了一个文件句柄,你可以但是让LockFileExwait直到它能够获得锁。这是默认设置,除非您提供LOCKFILE_FAIL_IMMEDIATELY。另请注意,虽然 ... ...(续)锁定 per se 当然可以通过 only 对文件的读取访问权限,显然如果您真的想写入该文件,你最好用写权限打开它。否则,无论文件是否被锁定,您的写入都不会成功。 我还是不清楚。这是否意味着无法等待文件可供写入? @TomášZato:如果不编写设备驱动程序,就不可能以阻塞方式打开文件句柄。 Windows API 允许您异步执行几乎所有操作并等待完成(但有时它会对您撒谎。例如完成读取“立即”,这并不像单词所暗示的那样立即)。一切,除了,打开文件句柄CreateFile 要么成功,要么不成功。所以,你可以等LockFileEx,没问题。但是首先你需要一个句柄......所以如果 "cannot do" 不是一个选项,你必须尝试直到成功。【参考方案2】:

除了一个繁忙的循环之外,反复尝试使用写访问权限打开文件(这听起来不对 - 如果文件被卡住并需要重新启动或手动终止的进程锁定,您将永远不会可以给它写信。

您可以写入一个临时文件并在之后重命名它(您可以告诉操作系统需要进行文件重命名操作,它会在下次启动时执行此操作)。如果您需要追加而不是写入,那么您必须编写一个进程以将您的临时文件追加到正确的文件中,可能在启动时(将哪个文件的说明写入您的进程读取的文件的位置) .

如果您需要修改锁定的文件,那么您只需尽快锁定它,如果您没有写入权限,则拒绝启动程序 - 警告用户权限开始。

您有可能以更好的方式等待:如果文件被锁定以进行写入,您可以假设有人要写入它,因此使用 FindFirstChangeNotification 接收 FILE_NOTIFY_CHANGE_LAST_WRITE 或 FILE_NOTIFY_CHANGE_ATTRIBUTES 事件的事件。它并不完美,因为有人也可以请求独占阅读权限。

我想您可以尝试获取锁定文件的句柄并等待它,因此当它被释放时,您的 WaitForSingleObject 将返回。但是,很有可能不允许您获得由不同进程(由安全子系统)拥有的句柄

【讨论】:

我考虑过文件可以被无限锁定。我现在更担心/“种族”条件......如果有例如,这似乎有点不公平。运行 2 个不同的循环,其中 1 个循环比另一个更快,并且总是“窃取”一个句柄,这让第二个循环永远不会转弯。我认为有一种解决方案可以“保留”一次句柄并检查它,而不是使用CreateFile

以上是关于等待文件可写的主要内容,如果未能解决你的问题,请参考以下文章

PHP判断文件或者目录是否可写

C#:检查文件是不是未锁定和可写

如何检查 Windows 文件是不是可读/可写?

将 perforce 中未跟踪的文件夹和文件设置为可写

python学习二十一天文件可读,可写,可执行的操作

如何对从 AWS OpsWorks 部署的应用程序拥有组可写文件权限?