Wer verbraucht meine inotifizierten Ressourcen?

49

Nach einem kürzlich durchgeführten Upgrade auf Fedora 15 stelle ich fest, dass eine Reihe von Tools mit Fehlern wie den folgenden ausfällt:

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

Es ist auch nicht nur so, taildass Probleme mit inotify gemeldet werden. Gibt es eine Möglichkeit, den Kernel abzufragen, um herauszufinden, welche Prozesse die inotifizierten Ressourcen verbrauchen? Die aktuellen inotify-bezogenen sysctlEinstellungen sehen folgendermaßen aus:

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

Antworten:

39

Wenn der Prozess über inotify_init () eine inotify-Instanz erstellt, ist die resultierende Datei, die den Dateideskriptor im / proc-Dateisystem darstellt, anscheinend ein Symlink zur (nicht vorhandenen) Datei '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

Sofern ich das Konzept nicht falsch verstanden habe, sollte der folgende Befehl eine Liste der Prozesse (deren Darstellung in / proc) anzeigen, sortiert nach der Anzahl der von ihnen verwendeten inotify-Instanzen.

for foo in /proc/*/fd/*; do readlink -f $foo; done | grep inotify | sort | uniq -c | sort -nr
Petr Uzel
quelle
8
Großartig, vielen Dank! Ich wusste nichts über die inotify-Inodes, die in / proc auftauchen. Für meine Zwecke kann der Befehl so vereinfacht werden:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks
Ich bin froh, dass es geholfen hat. Und deine Lösung mit find -lname ist in der Tat viel netter als meine mit for loop und readlink.
Petr Uzel
3
Beachten Sie, dass Sie möglicherweise nicht mehr auf dem Laufenden sind (keine Instanzen). Auf meinem System gibt es zum Beispiel eine geringe Anzahl von Instanzen, aber es gibt viele Zehntausende von Uhren von der KDE-Desktopsuche. Es ist schade, dass es keinen einfacheren Weg gibt, um zu überprüfen, wie viele Uhren / Instanzen verwendet werden, da der Kernel eindeutig weiß ...
Derobert
So zeigen Sie die Befehlszeilen der fehlerhaften Programme an:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan
@derobert Ich habe ein Skript erstellt, um die Prozesse aufzulisten, die Beobachter verbrauchen. Siehe meine Antwort unten.
Oligofren
25

Wahrscheinlich gehen Ihnen nicht nur Instanzen, sondern auch inotify- Uhren aus . So finden Sie heraus, wer viele Uhren herstellt:

  1. Haben echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enableVerfolgung der Uhr zu ermöglichen , ergänzt;
  2. Tun , cat /sys/kernel/debug/tracing/tracing_enabledum sicherzustellen , dass es auf 1 gesetzt , und wenn es nicht tun wird echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Starten Sie die Prozesse mit inotify-Instanzen neu (wie in der Antwort von Petr Uzel beschrieben), von denen Sie vermuten, dass sie viele Watches erstellen. und
  4. Lesen Sie die Datei /sys/kernel/debug/tracing/trace, um zu sehen, wie viele Uhren erstellt werden und von welchen Prozessen.

Wenn Sie fertig sind, stellen Sie sicher, dass Sie 0 in die Aktivierungsdatei (und die Datei "tracing_enabled", falls Sie diese ebenfalls aktivieren mussten) eingeben, um die Ablaufverfolgung zu deaktivieren, damit Sie nicht die Leistungseinbußen beim Fortsetzen der Ablaufverfolgung erleiden.

Jonathan Kamens
quelle
Es war eine Backup-Anwendung, die viele inotify-Uhren erstellte, und die Lösung in der akzeptierten Antwort half, den Täter zu identifizieren. Bisher war ich jedoch nicht mit der Systemaufrufverfolgung vertraut, die Sie hier demonstriert haben. Sehr cool. Danke für die Auskunft!
Larsks
2
Sind Sie sicher, dass es sich um '/ sys / kernel / debug / tracing / tracing_enabled' handelt? Auf meinem System scheint der richtige Pfad '/ sys / kernel / debug / tracing / tracing_on' zu sein ...
Kartoch
Es gibt kein / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / enable noch / sys / kernel / debug / tracing / tracing_enabled unter Gentoo Linux, aber / sys / kernel / debug / tracing / tracing_enabled existiert. Warum ist das so?
Zeekvfu
Wie @Kartoch impliziert, müssen Sie echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_onauf modernen Distributionen (Ubuntu 18.04.2 LTS) arbeiten.
Oligofren
Es reichte nicht aus, die Befehle für mich auszuführen, ich musste auch Folgendes tun: `cd / sys / kernel / debug / tracing /; Echofunktion> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren
7

Wie @ Jonathan Kamens sagte, gehen Ihnen wahrscheinlich die Uhren aus. Ich habe einen vorgefertigten Skript , inotify-consumers, dass die Listen dies für Sie:

$ 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

Hier sehen Sie schnell, warum die Standardbeschränkung von 8K-Beobachtern auf einem Entwicklungscomputer zu gering ist, da nur die WebStorm-Instanz diese schnell ausschöpft, wenn sie auf einen node_modulesOrdner mit Tausenden von Ordnern stößt . Fügen Sie einen Webpack-Beobachter hinzu, um Probleme zu garantieren ...

Kopieren Sie einfach den Inhalt des Skripts (oder die Datei auf GitHub) und legen Sie sie irgendwo in Ihrem $PATH, wie /usr/local/bin. Als Referenz ist der Hauptinhalt des Skripts einfach dies

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

Wenn Sie sich fragen, wie Sie die Grenzwerte erhöhen können, gehen Sie wie folgt vor, um sie dauerhaft zu machen:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Oligofren
quelle
1
Viele andere Vorschläge haben bei mir nicht gut funktioniert, aber dieses Skript hat bei Fedora 29 hervorragend funktioniert. Danke!
Richard S. Hall
6

Ich bin auf dieses Problem gestoßen, und keine dieser Antworten gibt Ihnen die Antwort "Wie viele Uhren verwendet jeder Prozess derzeit?" Die Einzeiler geben an, wie viele Instanzen geöffnet sind, was nur ein Teil der Geschichte ist, und das Trace-Zeug ist nur nützlich, um zu sehen, wie neue Uhren geöffnet werden.

TL; DR: Hiermit erhalten Sie eine Datei mit einer Liste der offenen inotifyInstanzen und der Anzahl der darin enthaltenen Überwachungen sowie den Pids und Binärdateien, die sie hervorgebracht haben, sortiert in absteigender Reihenfolge nach Anzahl der Überwachungen:

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

Das ist eine große Sauerei, also bin ich hierher gekommen. Um zu beginnen, habe ich taileine Testdatei ausgeführt und mir die FDS angesehen, die geöffnet wurden:

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

Also, 4 ist der fd, den wir untersuchen wollen. Mal sehen was drin ist 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

Das sieht aus wie ein Eintrag für die Uhr unten!

Versuchen wir etwas mit mehr Uhren, diesmal mit dem inotifywaitHilfsprogramm, und schauen uns an, was auch immer drin ist /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

Aha! Weitere Einträge! Also sollten wir sechs Dinge in /tmpdann haben:

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

Ausgezeichnet. Mein neuer inotifywaithat einen Eintrag in seiner fdListe (was die anderen Einzeiler hier zählen), aber sechs Einträge in seiner fdinfoDatei. So können wir herausfinden, wie viele Uhren ein bestimmter FD für einen bestimmten Prozess verwendet, indem wir seine fdinfoDatei konsultieren . Um es nun mit einigen der oben genannten zusammenzufassen, greifen Sie auf eine Liste der Prozesse zu, bei denen Benachrichtigungsüberwachungen geöffnet sind, und verwenden Sie diese, um die Einträge in den einzelnen zu zählen fdinfo. Das ist ähnlich wie oben, also werde ich den Einzeiler hier einfach wegwerfen:

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

Es gibt einige dicke Sachen hier, aber die Grundlagen sind, dass ich awkeinen fdinfoPfad von der lsofAusgabe aufbaue, die pid- und fd-Nummer greife und das u / r / w-Flag von letzterer abziehe. Dann fdinfozähle ich für jeden konstruierten Pfad die Anzahl der inotifyZeilen und gebe die Anzahl und die PID aus.

Es wäre schön, wenn ich hätte, welche Prozesse diese Pids an der gleichen Stelle darstellen, nicht wahr? Ich dachte auch. Also, in einem besonders chaotisch Bit, ließ ich mich auf Aufruf dirnamezweimal auf dem fdinfoWeg zu bekommen Pack /proc/<pid>, das Hinzufügen /exezu, und dann laufen readlinkauf , dass den exe - Namen des Prozesses zu erhalten. Werfen Sie das auch hinein, sortieren Sie es nach der Anzahl der Uhren und leiten Sie es zur sicheren Aufbewahrung in eine Datei um. Wir erhalten:

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

Wenn ich das ohne sudo ausführe, um nur meine oben gestarteten Prozesse zu zeigen, erhalte ich:

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

Perfekt! Eine Liste von Prozessen, FDS und wie viele Uhren jeder verwendet, genau das, was ich brauchte.

Cincodenada
quelle
Bei Verwendung lsoffür diesen Zweck würde ich empfehlen, die -nPFlags zu verwenden, um unnötige Lookups von Reverse-DNS- und Portnamen zu vermeiden. In diesem speziellen Fall wird auch das Hinzufügen -bwempfohlen, um ein potenzielles Blockieren von Systemaufrufen zu vermeiden. Das heißt, da lsofich auf meiner bescheidenen Workstation 3 Sekunden Zeit für die Wanduhr verschlungen habe (von denen 2 Sekunden im Kernel verbracht werden), ist dieser Ansatz gut für die Erkundung, aber leider ungeeignet für Überwachungszwecke.
BertD
Ich habe festgestellt, dass Ihr Einzeiler extrem langsam ist, aber es gibt eine nette Verbesserung, die auf Kosten des Informationsverlusts möglich ist (wir sehen die Beobachter pro Prozess und nicht pro Dateideskriptor): Erstellen Sie zuerst eine Zwischendatei: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-othencat 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

Zu verfolgen , welche Prozesse inotify verbrauchen Uhren (keine Instanzen) können Sie die dynamische Ftrace Funktion des Kernels verwenden , wenn es in Ihrem Kernel aktiviert ist.

Die Kernel-Option, die Sie benötigen, ist CONFIG_DYNAMIC_FTRACE.

Hängen Sie zuerst das debugfs-Dateisystem ein, falls es noch nicht angehängt ist.

mount -t debugfs nodev /sys/kernel/debug

Gehen Sie in das tracingUnterverzeichnis dieses debugfs-Verzeichnisses

cd /sys/kernel/debug/tracing

Aktivieren Sie das Tracing von Funktionsaufrufen

echo function > current_tracer

Filtern Sie nur SyS_inotify_add_watchSystemaufrufe

echo SyS_inotify_add_watch > set_ftrace_filter

Löschen Sie den Trace-Ringpuffer, wenn er nicht leer war

echo > trace

Aktivieren Sie die Ablaufverfolgung, wenn sie noch nicht aktiviert ist

echo 1 > tracing_on

Starten Sie den verdächtigen Prozess neu (in meinem Fall war es Crashplan, eine Sicherungsanwendung)

Beobachten Sie, wie die inotify_watch erschöpft ist

wc -l trace
cat trace

Getan

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

Ich habe das Skript oben geändert, um die Liste der Prozesse anzuzeigen, die inotify- Ressourcen verbrauchen :

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

Ich denke, es gibt eine Möglichkeit, mein doppeltes Sed zu ersetzen .


Ja. Benutze das eine oder das andere

cut -f 3 -d '/'   

oder

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

und du bekommst nur die pid.
Auch wenn Sie hinzufügen

2> /dev/null  

Im Fund werden Sie alle lästigen Fehlerzeilen los, die vom Fund geworfen werden. Das würde also funktionieren:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
Arkadij Kuzhel
quelle