处理这个 inotify 比赛的正确方法?

Right way to handle this inotify race?

我想维护一个镜像特定目录的缓存,所以我添加了一个手表,其事件由线程 A 监视,然后告诉线程 B 扫描该目录并将文件名放入我的缓存中。我有单独的线程,因为我希望应用程序在扫描期间仍然响应传入的 inotify 事件。否则,我可能会丢失事件,因为我没有读取它们并且 inotify 队列在扫描期间已满。

文件的删除或 move_from 事件完全有可能在目录扫描添加到我的缓存之前被处理。在那种情况下,天真的实现最终会拥有一个引用不存在的文件的缓存条目。处理这种特定竞争条件的正确方法是什么?

我的做法是保留两个永久线程:一个 实用线程 和一个 inotify 线程 非- 停止读取 inotify 文件描述符。这些线程通过阻塞队列进行通信。

inotify thread 检测到一个事件时,该事件可以是以下两种事件类型之一:

  1. 一个事件,表明必须销毁并重新创建所观察目录的整个缓存:队列溢出或卸载。
  2. 一个事件,可以通过更改缓存中的单个条目来处理(大多数其他 inotify 事件)

一经检测,事件立即排队到 实用程序线程

实用程序线程 接收到第一种类型的事件时,它会通过将完整的目录内容读入缓存来从头开始重新创建整个缓存。当还没有缓存并且第二类事件到达时,也会发生同样的情况。其他情况下full readdir()被避免,缓存只是根据事件修改。


您问题中描述的竞争条件只有在允许多个线程修改缓存时才会发生。所描述的方法通过假设唯一允许修改缓存的线程是 utility thread.

来处理它

如果你想允许其他线程修改缓存(例如,因为你不知道文件系统是否支持inotify),你可以使用更简单和更健壮的方法:不跟踪个别目录修改事件并让 实用程序线程 对每个到达的事件执行完整的 readdir()。最坏的情况是 readdirs 太多了,但是自己读取目录内容太便宜了,我不会在乎那个。

如果读取完整的目录内容并不便宜(例如,因为它可能非常非常大),那么您不应该将所有内容都存储在内存中以开始和。这种情况最好使用小的部分缓存,可以通过使用 telldirseekfstat 快速刷新以跟踪用户当前可见的少量文件。