Contents

inotify简介并解决“inotify 监控达到上限!” 错误

1. 简介

现代软件通常会根据数据变化自动更新视图:

这种便利是由不同的系统提供的。

在本教程中,我们将讨论inotify系统及其功能。之后,我们探索检查其当前用户的方法。最后,我们讨论一些与inotify相关的常见错误。

我们使用 GNU Bash 5.1.4 在 Debian 11 (Bullseye) 上测试了本教程中的代码。它是 POSIX 兼容的,应该可以在任何这样的环境中工作。

2. 文件系统事件通知

我们已经将索引节点 作为本地 Linux 文件系统中的一个键进行了讨论。事实上,它们的变化与许多文件事件有关。

基于此,inotify 子系统的主要功能是监视和报告文件系统中的事件。无论是借助inotifywaitinotifywatch 工具,还是其他软件的syscall ,我们都可以轻松掌握文件操作。

例如,我们可以使用命令行来简单地等待给定文件上的任何事件:

$ inotifywait /file.ext && echo 'Event.'
Setting up watches.
Watches established.

因此,如果*/file.ext*现在是任何操作的一部分,我们将收到通知。但是,如果我们想一次永久地对许多对象执行此操作怎么办?

3. 监控多个文件和目录树

有时我们需要更全面的监控。一种方法是将*–recursive* ( -r ) 和*–monitor* ( -m ) 开关添加到inotifywait。它们分别确保目录树处理和持续监控。

此外,我们可以使用inotifywatch来监听与文件系统的给定部分链接的事件:

$ inotifywatch /
Establishing watches...
Finished establishing watches, now collecting statistics.

由于每个文件系统对象都需要一个单独的 watch,因此 Linux 对其最大数量有一个上限。事实上,我们可以检查给定系统中的内容:

$ cat /proc/sys/fs/inotify/max_user_watches
524288

在这里,系统允许524288 (2^19)个监控 。方便的是,我们可以随时看到谁在使用它们。

4. 检查inotify用户

通过组合几个 Linux 命令,我们可以找出当前使用 watches 的进程

$ inotifywatch / &
Establishing watches...
Finished establishing watches, now collecting statistics.
[1] 666
$ find /proc/*/fd -lname anon_inode:inotify |
  cut -d/ -f3 |
  xargs -I '{}' -- ps --no-headers -o '%p %U %c' -p '{}' |
  uniq -c |
  sort -nr
      1    6660 root     inotifywatch

** inotify文件描述符的数量与 watches 的数量不同**。因此,有一个脚本 ,其中包含上述命令行的更好版本以及其他功能。

此外,我们可以使用strace (系统跟踪)来获取所有监控的列表:

$ strace --follow-forks --trace='inotify_add_watch' inotifywait --quiet /file.ext
inotify_add_watch(3, "/file.ext",
  IN_ACCESS|IN_MODIFY|IN_ATTRIB|IN_CLOSE_WRITE|IN_CLOSE_NOWRITE|IN_OPEN|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF) = 1

最后,还有lsof (List Open Files)工具,它也列出了inotify用户:

$ tail -f /file.ext &
[1] 666
$ lsof | grep inotify
tail    666             root    4r  a_inode               0,11        0            15387 inotify

即使有各种使用和处理监控的方法,问题也并不少见。

5. 常见的inotify错误

使用inotify子系统时有一些陷阱。让我们讨论其中的一些。

5.1. 权限

事实上,与其他文件系统操作一样,我们至少需要对目标对象有一些控制。换句话说,我们无法在没有适当权限的情况下进行监控

$ inotifywatch /root
Establishing watches...
Failed to watch /root: Permission denied

要解决这个问题,我们至少需要能够读取目标对象。在这里,我们通过chmod (更改模式)添加必要的权限:

$ chmod +r /root

完成这些更改后,我们可以添加监控。

5.2. inotify监视限制

考虑到 / 上的递归inotifywatch会占用与文件一样多的监控。事实上,在文件数量超过监视限制的文件系统上执行此操作会产生错误:

$ cat /proc/sys/fs/inotify/max_user_watches
524288<
$ df --inodes /
Filesystem       Inodes  IUsed    IFree IUse% Mounted on
/dev/sdb       16777216 666000 16111216    4% /
$ inotifywatch --recursive /
Establishing watches...
Failed to watch /; upper limit on inotify watches reached!
Please increase the amount of inotify watches allowed per user via '/proc/sys/fs/inotify/max_user_watches'.

当然,解决方案在错误文本中。但是,应用它的方法不止一种:

$ max_user_watches=CUSTOM_MAX_USER_WATCHES_VALUE
$ sysctl fs.inotify.max_user_watches=${max_user_watches}
$ echo fs.inotify.max_user_watches=${max_user_watches} >> /etc/sysctl.conf
$ sysctl --load

虽然第一个命令在每个会话中应用它,但第二个命令在重启后仍然存在。那是因为它向*/etc/sysctl.conf*添加了一行。但是,如果该行已经存在,我们应该根据需要对其进行更改。我们可以避免使用最后一个命令重新启动,该命令重新读取并应用配置文件。

5.3. 主内存 (RAM) 问题

因为每个 watch 都是一个结构体,*可用内存也是使用inotify 的瓶颈。事实上,一个监控最多可以占用 1KB 的空间。这意味着一百万个监控可能会导致 1GB 的额外 RAM 使用量。 有时,这会导致奇怪的错误:

  • inotify_add_watch 失败:设备上没有剩余空间 (/file.ext)
  • 无法监控*“/file.ext”*:设备上没有剩余空间

释放内存似乎是可行的方法。但是,这种方法有几个问题。

首先,** inotify使用无法交换的内核内存 **。其次,即使监控刚用完,程序也可能会抛出此类错误。