Inotify - 一个强大而简单的文件变更通知系统¶
文档于 2005 年 3 月 15 日由 Robert Love <rml@novell.com> 开始编写
文档于 2015 年 1 月 4 日由 Zhang Zhen <zhenzhang.zhang@huawei.com> 更新
删除了过时的接口,用户界面请参考手册页。
基本原理
- 问
为什么不将监视与被监视对象的打开的文件描述符绑定在一起?其背后的设计决策是什么?
- 答
监视与打开的 inotify 设备相关联,而不是打开的文件。这解决了 dnotify 的主要问题:保持文件打开会锁定文件,因此更糟糕的是,会锁定挂载点。因此,dnotify 不适用于带有可移动媒体的桌面系统,因为媒体无法卸载。监视文件不应要求文件处于打开状态。
- 问
为什么使用每个实例一个 fd 而不是每个监视一个 fd?其背后的设计决策是什么?
- 答
每个监视一个 fd 会快速消耗比允许的更多的文件描述符,比可管理的更多 fd,以及比最优的 select() 可选择的更多 fd。是的,root 可以增加每个进程的 fd 限制,是的,用户可以使用 epoll,但是同时要求两者都是愚蠢且不必要的要求。一个监视消耗的内存比一个打开的文件少,因此分隔数量空间是明智的。当前的设计是用户空间开发人员想要的:用户初始化 inotify 一次,并添加 n 个监视,只需要一个 fd,并且无需调整 fd 限制。初始化 inotify 实例两千次是愚蠢的。如果我们能够干净地实现用户空间的偏好——而且我们可以,idr 层使这类事情变得简单——那么我们就应该这样做。
还有其他很好的论据。使用单个 fd,有一个要阻塞的单个项,它映射到单个事件队列。单个 fd 返回所有监视事件以及任何潜在的带外数据。如果每个 fd 都是一个单独的监视,
将无法获得事件排序。文件 foo 和文件 bar 上的事件会在两个 fd 上弹出 poll(),但无法判断哪个事件先发生。单个队列可以轻松地为您提供排序。这种排序对于现有应用程序(例如 Beagle)至关重要。想象一下,没有排序的 “mv a b ; mv b a” 事件。
我们将不得不维护 n 个 fd 和 n 个带有状态的内部队列,而不是只有一个。在内核中会更加混乱。单个线性队列是合理的数据结构。
用户空间开发人员更喜欢当前的 API。例如,Beagle 的开发人员喜欢它。相信我,我问过了。这并不奇怪:谁想通过 select 管理和阻塞 1000 个 fd?
无法获得带外数据。
1024 仍然太低了。;-)
当您谈论设计一个可扩展到数千个目录的文件变更通知系统时,管理数千个 fd 似乎不是正确的接口。它太笨重了。
此外,可以有多个实例并管理多个队列,从而管理多个关联的 fd。不必存在每个进程一个 fd 的映射;它是每个队列一个 fd,并且一个进程很容易想要多个队列。
- 问
为什么采用系统调用方法?
- 答
糟糕的用户空间接口是 dnotify 的第二个最大问题。信号是一个糟糕的文件通知接口。或者,就此而言,对于任何事情都是如此。从所有角度来看,理想的解决方案是一个基于文件描述符的解决方案,它允许基本的文件 I/O 和 poll/select。获取 fd 和管理监视可以通过设备文件或一系列新的系统调用来完成。我们决定实现一系列系统调用,因为这是新内核接口的首选方法。唯一的真正区别是我们是否要使用 open(2) 和 ioctl(2) 或几个新的系统调用。系统调用优于 ioctl。