inotifyリソースを消費しているのは誰ですか?


49

Fedora 15に最近アップグレードした後、次の行に沿っていくつかのツールがエラーで失敗していることがわかりました。

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

tailinotifyの問題を報告しているだけではありません。カーネルに問い合わせて、どのプロセスがinotifyリソースを消費しているかを調べる方法はありますか?現在のinotify関連のsysctl設定は次のようになります。

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

回答:


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

概念を誤解しない限り、次のコマンドは、使用するinotifyインスタンスの数でソートされたプロセスのリスト(/ procの表現)を表示する必要があります。

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

8
優れたありがとう!/ procに表示されるinotify iノードについては知りませんでした。私の目的のために、コマンドはこれに単純化できますfind /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
。– larsks

助けてくれてうれしいです。そして、find -lnameを使用したソリューションは、forループとreadlinkを使用したソリューションよりもはるかに優れています。
ペトルウーゼル

3
(インスタンスではなく)監視対象外になる可能性があることに注意してください。たとえば、私のシステムでは、インスタンス数は10分の1になりますが、KDEのデスクトップ検索からの何万もの時計があります。カーネルが明確に認識しているため、使用中のウォッチ/インスタンスの数を確認する簡単な方法はありません
...-derobert

問題のあるプログラムのコマンドラインを表示するには:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan

@derobertウォッチャーを消費するプロセスをリストするスクリプトを作成しました。これは通常、気にすることです。以下の私の答えをご覧ください。
オリゴフレン

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に設定し、それがそうされていない場合のmakeしますecho 1 >> /sys/kernel/debug/tracing/tracing_enabled
  3. 多くのウォッチを作成する疑いがあるinotifyインスタンス(Petr Uzelの答えで説明されているように決定)でプロセスを再起動します。そして
  4. ファイル/sys/kernel/debug/tracing/traceを読んで、作成されたウォッチの数とプロセスを確認します。

完了したら、有効化ファイル(および有効化する必要がある場合は、tracing_enabledファイル)に0をエコーし​​て、トレースをオフにしてパフォーマンスを低下させないようにしてください。


これは多くのinotifyウォッチを作成するバックアップアプリケーションであり、受け入れられた回答のソリューションが犯人の特定に役立ちました。ただし、ここで紹介したシステムコールトレースについては、これまで詳しくありませんでした。とてもかっこいい。情報のおかげで!
ラルスク

2
「/ sys / kernel / debug / tracing / tracing_enabled」であると確信していますか?私のシステムでは、正しいパスは '/ sys / kernel / debug / tracing / tracing_on'のようです
...-Kartoch

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

@Kartochが示すように、echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on最新のディストリビューション(Ubuntu 18.04.2 LTS)で行う必要があります。
オリゴフレン

私に代わってコマンドを実行するだけでは不十分でした。また、次の操作を行う必要がありました。エコー機能> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
オリゴフレン

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

あぁ!その他のエントリー!したがって、その時点で6つのことが必要/tmpです。

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

優れた。私の新しいがinotifywaitあり1つの、その中のエントリfd(ここでは、他のワンライナーがカウントされているものです)のリストが、その内6つのエントリ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を構築し、pidとfd番号を取得し、後者からu / r / wフラグを削除することです。次に、構築されたパスごとに、行数をカウントし、カウントとpidを出力します。fdinfolsoffdinfoinotify

ただし、これらのpidがどのプロセスを同じプロセスで表すのかがあればいいですね。私はそうだと思いました。そのため、特に厄介な点で、packを取得するためにパスをdirname2回呼び出して追加し、それを実行しプロセスのexe名を取得することに決めました。そこにもそれを投げ、時計の数でソートし、安全に保つためにファイルにリダイレクトすると、次のようになります:fdinfo/proc/<pid>/exereadlink

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.*$'/''/`

私のダブルセッドを交換する方法があると思います。


はい。どちらかを使用

cut -f 3 -d '/'   

または

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

pidのみを取得します。
また、追加する場合

2> /dev/null  

findでは、findによってスローされる厄介なエラー行を取り除きます。したがって、これは動作します:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.