如果您想避免任何嘲笑,最好的办法就是直接达到操作系统限制来引发错误。例如,inotify_init 可能会失败并显示EMFILE errno,如果调用进程已达到其打开文件描述符数量的限制。要以 100% 的精度达到此类条件,您可以使用两个技巧:
- 通过changing values in procfs动态操作运行进程的限制
- 将您的应用进程分配给专用 cgroup 并通过 cgroups API 为其提供约 0% 的 CPU 时间来“暂停”它(这是 Android 限制后台应用程序并实现其节能“打盹”模式的方式)。
所有可能的 inotify 错误情况都记录在 inotify、inotify_init 和 inotify_add_watch 的手册页中(我认为 inotify_rm_watch 不会失败,除非您的代码中出现纯粹的编程错误)。
除了普通的错误(例如超过/proc/sys/fs/inotify/max_user_watches)之外,inotify 有几种故障模式(队列空间耗尽、手表 ID 重用),但这些都不是严格意义上的“故障”。
当有人执行文件系统更改的速度快于您的反应速度时,就会发生队列耗尽。很容易重现:使用 cgroups 在程序打开 inotify 描述符时暂停程序(因此事件队列不会耗尽),并通过修改观察到的文件/目录快速生成 lots 通知。一旦您拥有/proc/sys/fs/inotify/max_queued_events 的未处理事件,并取消暂停您的程序,它将收到IN_Q_OVERFLOW(并可能错过一些不适合队列的事件)。
Watch ID 重用很难重现,因为现代内核从类似于文件描述符的行为切换到类似于 PID 的 watch-ID 行为。您应该使用与测试 PID 重用时相同的方法——创建和销毁 lots 个 inotify 监视,直到整数监视 ID 环绕。
Inotify 也有一些棘手的极端情况,在正常操作期间很少发生(例如,我知道的所有 Java 绑定,包括 Android 和 OpenJDK,都不能正确处理所有这些):same-inode 问题和处理IN_UNMOUNT.
inotify 文档中很好地解释了同一个 inode 问题:
对 inotify_add_watch() 的成功调用会返回此 inotify 实例的唯一监视描述符,用于对应于路径名的文件系统对象(inode)。如果文件系统对象以前没有被这个 inotify 实例监视,那么监视描述符是新分配的。如果文件系统对象已被监视(可能通过指向同一对象的不同链接),则返回现有监视的描述符。
简单来说:如果您观看两个指向同一个文件的硬链接,它们的数字观看 ID 将是相同的。如果您将 watch 存储在 hashmap 之类的东西中,这种行为很容易导致丢失对第二个 inotify watch 的跟踪,并以整数 watch ID 为键。
第二个问题更难观察,因此即使不是错误模式也很少得到适当的支持:卸载分区,目前通过 inotify 观察到。棘手的部分是:Linux 文件系统不允许您在文件描述符打开它们时自行卸载,但通过 inotify 观察文件不会阻止文件系统卸载。如果您的应用在单独的文件系统上观察文件,并且用户卸载了该文件系统,则您必须准备好处理由此产生的 IN_UNMOUNT 事件。
以上所有测试都应该可以在 tmpfs 文件系统上执行。