谁在消耗我的资源化资源?


49

在最近升级到Fedora 15之后,我发现许多工具都因以下错误而出错:

tail: inotify resources exhausted
tail: inotify cannot be used, reverting to polling

也不仅仅是tail报告了inotify的问题。有什么方法可以查询内核以找出哪些进程正在消耗inotify资源?当前与inotify相关的sysctl设置如下所示:

fs.inotify.max_user_instances = 128
fs.inotify.max_user_watches = 8192
fs.inotify.max_queued_events = 16384

Answers:


39

看来,如果该进程通过inotify_init()创建了inotify实例,则表示/ proc文件系统中的filedescriptor的结果文件将是(不存在)“ anon_inode:inotify”文件的符号链接。

$ cd /proc/5317/fd
$ ls -l
total 0
lrwx------ 1 puzel users 64 Jun 24 10:36 0 -> /dev/pts/25
lrwx------ 1 puzel users 64 Jun 24 10:36 1 -> /dev/pts/25
lrwx------ 1 puzel users 64 Jun 24 10:36 2 -> /dev/pts/25
lr-x------ 1 puzel users 64 Jun 24 10:36 3 -> anon_inode:inotify
lr-x------ 1 puzel users 64 Jun 24 10:36 4 -> anon_inode:inotify

除非我误解了这个概念,否则以下命令应向您显示进程列表(它们在/ proc中的表示形式),并按它们使用的inotify实例数进行排序。

for foo in /proc/*/fd/*; do readlink -f $foo; done | grep inotify | sort | uniq -c | sort -nr

8
非常好,谢谢!我不知道在/ proc中显示的inotify索引节点。就我而言,命令可以简化为:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks 2011年

我很高兴它有所帮助。使用find -lname的解决方案确实比使用for循环和readlink的解决方案好得多。
Petr Uzel

3
请注意,您也可能不在监视范围内(而不是实例)。例如,在我的系统上,实例数量很少,但是KDE的桌面搜索有成千上万的手表。太糟糕了,没有一种更简便的方法可以检查正在使用的手表/实例的数量,因为内核显然知道……
derobert 2013年

要显示有问题的程序的命令行:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan

@derobert我编写了一个脚本来列出消耗观察者的进程,这通常是人们所关心的。请参阅下面的答案。
oligofren

25

你可能会用完的inotify的手表,而不是实例。要找出谁制造了很多手表:

  1. echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enable的手表增加,以使跟踪;
  2. 这样做cat /sys/kernel/debug/tracing/tracing_enabled,以确保它设置为1,如果不这样做echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. 使用您怀疑会创建大量手表的inotify实例(由Petr Uzel的答案确定)重新启动进程;和
  4. 读取文件/sys/kernel/debug/tracing/trace以查看创建了多少只手表以及通过哪些进程。

完成后,请确保在启用文件(以及必须同时启用tracing_enabled文件)中回显0以关闭跟踪,以免造成继续跟踪的性能下降。


它是一个备份应用程序,可创建许多inotify手表,并且已接受的答案中的解决方案有助于找出罪魁祸首。但是,我以前对您在此处演示的系统调用跟踪并不熟悉。很酷。谢谢提供信息!
larsk's

2
您确定它是'/ sys / kernel / debug / tracing / tracing_enabled'吗?在我的系统上,正确的路径似乎是'/ sys / kernel / debug / tracing / tracing_on'...
Kartoch 2013年

在Gentoo Linux上既没有 / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / enable也没有/ sys / kernel / debug / tracing / tracing_enabled,但是存在/ sys / kernel / debug / tracing / tracing_enabled。这是为什么?
zeekvfu 2013年

就像@Kartoch所暗示的那样,您需要进行echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on现代发行(Ubuntu 18.04.2 LTS)。
oligofren

仅仅为我做命令还不够,我还需要做:cd / sys / kernel / debug / tracing /; 回声函数> current_tracer; 回声SyS_inotify_add_watch> set_ftrace_filter`
oligofren

7

正如@Jonathan Kamens所说,您可能已经用完手表了。我有一个预制脚本inotify-consumers为您列出了该脚本

$ time inotify-consumers  | head

   INOTIFY
   WATCHER
    COUNT     PID     CMD
----------------------------------------
    6688    27262  /home/dvlpr/apps/WebStorm-2018.3.4/WebStorm-183.5429.34/bin/fsnotifier64
     411    27581  node /home/dvlpr/dev/kiwi-frontend/node_modules/.bin/webpack --config config/webpack.dev.js
      79     1541  /usr/lib/gnome-settings-daemon/gsd-xsettings
      30     1664  /usr/lib/gvfs/gvfsd-trash --spawner :1.22 /org/gtk/gvfs/exec_spaw/0
      14     1630  /usr/bin/gnome-software --gapplication-service

real    0m0.099s
user    0m0.042s
sys 0m0.062s

在这里,您可以快速了解为什么在开发计算机上默认的8K监视程序限制太小,因为遇到一个node_modules有成千上万个文件夹的文件夹时,只有WebStorm实例会很快将其最大化。添加一个webpack观察器以确保出现问题...

只需复制脚本的内容(或GitHub上的文件),然后将其放在您的某个位置即可$PATH,例如/usr/local/bin。仅供参考,脚本的主要内容如下:

find /proc/*/fd \
    -lname anon_inode:inotify \
    -printf '%hinfo/%f\n' 2>/dev/null \
    \
    | xargs grep -c '^inotify'  \
    | sort -n -t: -k2 -r 

如果您想知道如何增加限制,可以使用以下方法使其永久不变:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

1
许多其他建议对我来说效果不佳,但是此脚本在Fedora 29上效果很好。谢谢!
理查德·S·霍尔

6

我遇到了这个问题,这些答案都没有给您答案“ 每个进程当前使用多少只手表?”。一线便能为您提供打开实例的数量,这只是故事的一部分,而跟踪信息仅对于查看正在打开的新手表有用。

TL; DR:这将为您提供一个文件,其中包含打开的inotify实例列表以及它们拥有的监视数量,以及产生它们的pid和二进制文件,并按监视数量降序排列:

sudo lsof | awk '/anon_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | while read fdi; do count=$(sudo grep -c inotify $fdi); exe=$(sudo readlink $(dirname $(dirname $fdi))/exe); echo -e $count"\t"$fdi"\t"$exe; done | sort -nr > watches

那是一个很大的烂摊子,所以这就是我到达那里的方式。首先,我tail在一个测试文件上运行了一个文件,并查看了它打开的fd:

joel@gladstone:~$ tail -f test > /dev/null &
[3] 22734
joel@opx1:~$ ls -ahltr /proc/22734/fd
total 0
dr-xr-xr-x 9 joel joel  0 Feb 22 22:34 ..
dr-x------ 2 joel joel  0 Feb 22 22:34 .
lr-x------ 1 joel joel 64 Feb 22 22:35 4 -> anon_inode:inotify
lr-x------ 1 joel joel 64 Feb 22 22:35 3 -> /home/joel/test
lrwx------ 1 joel joel 64 Feb 22 22:35 2 -> /dev/pts/2
l-wx------ 1 joel joel 64 Feb 22 22:35 1 -> /dev/null
lrwx------ 1 joel joel 64 Feb 22 22:35 0 -> /dev/pts/2

因此,4是我们要调查的fd。让我们看看其中的含义fdinfo

joel@opx1:~$ cat /proc/22734/fdinfo/4
pos:    0
flags:  00
mnt_id: 11
inotify wd:1 ino:15f51d sdev:ca00003 mask:c06 ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:1df51500a75e538c

看起来像是手表底部的条目!

让我们尝试使用更多手表,这一次使用inotifywait实用程序,只看其中的内容/tmp

joel@gladstone:~$ inotifywait /tmp/* &
[4] 27862
joel@gladstone:~$ Setting up watches.
Watches established.
joel@gladstone:~$ ls -ahtlr /proc/27862/fd | grep inotify
lr-x------ 1 joel joel 64 Feb 22 22:41 3 -> anon_inode:inotify
joel@gladstone:~$ cat /proc/27862/fdinfo/3
pos:    0
flags:  00
mnt_id: 11
inotify wd:6 ino:7fdc sdev:ca00003 mask:fff ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:dc7f0000551e9d88
inotify wd:5 ino:7fcb sdev:ca00003 mask:fff ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:cb7f00005b1f9d88
inotify wd:4 ino:7fcc sdev:ca00003 mask:fff ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:cc7f00006a1d9d88
inotify wd:3 ino:7fc6 sdev:ca00003 mask:fff ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:c67f00005d1d9d88
inotify wd:2 ino:7fc7 sdev:ca00003 mask:fff ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:c77f0000461d9d88
inotify wd:1 ino:7fd7 sdev:ca00003 mask:fff ignored_mask:0 fhandle-bytes:8 fhandle-type:1 f_handle:d77f00000053c98b

啊哈!更多条目!所以/tmp那时我们应该有六件事:

joel@opx1:~$ ls /tmp/ | wc -l
6

优秀的。我的新产品在其列表中inotifywait一个条目fd(这是这里其他一线产品正在计算的),但是在其fdinfo文件中有六个条目。因此,我们可以通过查询fdinfo文件来确定给定进程的给定fd使用了多少手表。现在将其与上面的一些内容放在一起,以获取已打开通知手表的进程列表,并使用该进程对每个表中的条目进行计数fdinfo。这类似于上面的内容,因此我将在这里转储单层纸:

sudo lsof | awk '/anon_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | while read fdi; do count=$(sudo grep -c inotify $fdi); echo -e $count"\t"$fdi; done

这里有一些很厚的东西,但是基础是我用来从输出awk构建fdinfo路径lsof,抓取pid和fd数字,从后者中删除u / r / w标志。然后,对于每个构造的fdinfo路径,我计​​算行数inotify并输出计数和pid。

但是,如果我在同一个位置上具有这些pid所代表的进程,那会很好,对吗?我是这么想的。因此,在一个特别凌乱一点,我定居在呼吁dirname上两次fdinfo获得包到路径/proc/<pid>,加入/exe到它,然后运行readlink获得进程的exe文件名。同样将其扔到那里,按手表数量排序,然后将其重定向到文件中以进行安全保存,我们得到:

sudo lsof | awk '/anon_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | while read fdi; do count=$(sudo grep -c inotify $fdi); exe=$(sudo readlink $(dirname $(dirname $fdi))/exe); echo -e $count"\t"$fdi"\t"$exe; done | sort -n > watches

不使用 sudo的情况下运行它,只是显示我在上面启动的进程,我得到:

joel@gladstone:~$ cat watches 
6   /proc/4906/fdinfo/3 /usr/bin/inotifywait
1   /proc/22734/fdinfo/4    /usr/bin/tail

完善!进程,fd以及每个进程使用多少手表的列表,这正是我所需要的。


当使用lsof用于此目的,我会建议使用的-nP标志,以避免反向DNS和端口名称不必要的查找。在这种特殊情况下,-bw还建议添加以避免潜在的阻塞系统调用。就是说,lsof在我不起眼的工作站上浪费了3秒钟的挂钟时间(其中2秒钟用在内核上),这种方法很适合探索,但是不适合监视。
BertD

我发现您的单行代码非常慢,但是有一个很好的改进,可能会损失一些信息(我们将看到每个进程的观察者,而不是每个文件描述符的观察者):首先生成一个中间文件:lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-o然后cat uniq-o | while read fdi; do count=$(cat ${fdi}fdinfo/* | grep -c inotify 2>/dev/null); exe=$(readlink ${fdi}exe); echo -e $count"\t"${fdi}"\t"$exe; done > watches
LLlAMnYP

5

要跟踪哪个进程占用inotify的手表(没有实例),你可以使用内核的动态ftrace功能,如果它在你的内核中启用。

您需要的内核选项是CONFIG_DYNAMIC_FTRACE

如果尚未安装debugfs文件系统,请首先安装它。

mount -t debugfs nodev /sys/kernel/debug

转到tracing此debugfs目录的子目录下

cd /sys/kernel/debug/tracing

启用函数调用跟踪

echo function > current_tracer

仅过滤SyS_inotify_add_watch系统调用

echo SyS_inotify_add_watch > set_ftrace_filter

清除跟踪环缓冲区(如果不为空)

echo > trace

启用跟踪(如果尚未启用)

echo 1 > tracing_on

重新启动可疑进程(在我的情况下是崩溃计划,这是一个备份应用程序)

观看inotify_watch耗尽

wc -l trace
cat trace

完成了


3
find /proc/*/fd/* -type l -lname 'anon_inode:inotify' 2>/dev/null | cut -f 1-4 -d'/' |  sort | uniq -c  | sort -nr

1

我修改了上面显示的脚本,以显示消耗inotify资源的进程列表:

ps -p `find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print | sed s/'^\/proc\/'/''/ | sed s/'\/fd.*$'/''/`

我认为有一种方法可以替换我的double sed


是。使用任一

cut -f 3 -d '/'   

要么

sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1'  

而且您只会得到pid。
另外,如果您添加

2> /dev/null  

在查找中,您将摆脱查找引发的任何讨厌的错误行。所以这将工作:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.