【问题标题】:Right way to handle this inotify race?处理这个inotify比赛的正确方法?
【发布时间】:2018-04-17 16:19:47
【问题描述】:

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

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

【问题讨论】:

  • 事情实际上比你想象的还要糟糕:目录可以在 readdir 调用期间发生变化,从而导致结果中的文件条目丢失或重复。但这在实践中并不重要——只需将File#list 的结果视为文件列表,可能 在那里并充分处理任何FileNotFoundExceptions。更重要的是-从您的问题看来,您正在尝试从两个线程中修改缓存。不要那样做。让您的 inotify 循环线程从 inotify 描述符中读取;当偶数出现时,向阅读器线程发送通知,以便它为您修改缓存。
  • 我不确定你在说什么,为什么 inotify 阅读器线程会向临时目录阅读器线程发送一些东西?即使只有一个线程实际管理缓存,您仍然会遇到排序问题。如果管理缓存的任何线程接收到 delete 或 move_from 事件(删除不存在的缓存条目),然后是添加到缓存事件(来自临时 readdir 线程),添加到缓存事件会创建一个引用文件的缓存条目已经被删除了。解决方案是什么,在删除时创建一个缓存条目并在添加时删除它?逻辑不通。
  • "如果管理缓存的任何线程接收到 delete 或 move_from 事件(删除不存在的缓存条目),然后是添加到缓存事件(来自临时 readdir 线程)" - 你为什么突然引入第三个线程?您的问题描述了从描述符读取的单个线程和执行 readdir 的另一个线程......没有理由将更多线程引入方程式。即使是第二个线程也大多是不必要的。 “我不确定……为什么 inotify 阅读器线程会发送一些东西”——在尝试进行更多的多线程编程之前,请阅读有关消息队列的信息
  • 我没有添加第三个线程。有一个 inotify 线程和一个 readdir 线程。我想象后者是暂时的,将“找到文件”事件发送给另一个,然后退出。无论谁发送给谁或使用何种通信机制,都需要解决竞赛。如果我确定在使用 readdir 处理目录时 inotify 队列无法填满并开始丢弃事件,我就不会再为第二个线程而烦恼了。
  • 啊,我明白了。发送多个“找到文件”事件会相当昂贵,所以我没有想到这种情况。相反,我想,您的 readdir 线程只是直接清除特定目录的缓存并从 readdir() 结果重新填充它(如果在此期间有新事件到达,inotify 线程会取消预定的 readdir())。

标签: linux inotify


【解决方案1】:

我的做法是保留两个永久线程:一个实用程序线程和一个inotify 线程,用于不间断地从inotify 文件描述符中读取。这些线程通过阻塞队列进行通信。

inotify 线程 检测到一个事件时,它可以是两种事件类型之一:

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

一经检测,该事件立即排入实用程序线程

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


只有在允许多个线程修改缓存的情况下,您的问题中描述的竞争条件才可能发生。所描述的方法通过假设唯一允许修改缓存的线程是实用线程来处理它。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 2019-08-19
    • 2010-12-08
    • 2011-02-28
    • 2016-01-15
    • 1970-01-01
    相关资源
    最近更新 更多