Qui consomme mes ressources inotify?

49

Après une récente mise à niveau vers Fedora 15, je constate que plusieurs outils échouent avec des erreurs telles que:

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

Ce n'est pas simplement tailque signaler des problèmes avec inotify non plus. Existe-t-il un moyen d'interroger le noyau pour savoir quel processus utilise les ressources inotify? Les sysctlparamètres actuels liés à inotify ressemblent à ceci:

fs.inotify.max_user_instances = 128
fs.inotify.max_user_watches = 8192
fs.inotify.max_queued_events = 16384
alsacs
la source

Réponses:

39

Il semble que si le processus crée une instance inotify via inotify_init (), le fichier résultant qui représente filedescriptor dans le système de fichiers / proc est un lien symbolique vers le fichier (non existant) '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

Sauf si j'ai mal compris le concept, la commande suivante devrait vous montrer la liste des processus (leur représentation dans / proc), triés par nombre d'instances inotify qu'ils utilisent.

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

Trouver les coupables

Via les commentaires ci-dessous, @markkcowan a mentionné ceci:

$ find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Petr Uzel
la source
8
Excellent merci! Je ne connaissais pas les inodes inotify apparaissant dans / proc. Pour ce qui me concerne, la commande peut être simplifiée à ceci:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
mollusques
Je suis content que cela ait aidé. Et votre solution avec find -lname est en effet beaucoup plus agréable que la mienne avec pour boucle et readlink.
Petr Uzel
4
Notez que vous pouvez également être à court de montres (pas d'instances). Par exemple, sur mon système, le nombre d'instances est faible, mais il existe plusieurs dizaines de milliers de montres issues de la recherche sur le bureau de KDE. Dommage qu'il n'y ait pas de moyen plus facile de vérifier le nombre de surveillances / instances utilisées, car le noyau sait clairement que ...
derobert
Pour afficher les lignes de commande des programmes en cause:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan
@derobert J'ai créé un script pour répertorier les processus qui consomment des observateurs, ce qui est généralement ce qui importe. Voir ma réponse ci-dessous.
oligofren le
25

Vous êtes probablement à court de montres inotify plutôt que d'instances. Pour savoir qui crée beaucoup de montres:

  1. Faire echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enablepour permettre le traçage des annonces de surveillance;
  2. Faites cat /sys/kernel/debug/tracing/tracing_enabledpour vous assurer qu'il est mis à 1 et si elle est pas fait echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Redémarrez les processus avec les instances inotify (déterminées comme décrit dans la réponse de Petr Uzel) que vous suspectez de créer de nombreuses surveillances. et
  4. Lisez le fichier /sys/kernel/debug/tracing/tracepour voir combien de montres sont créées et par quels processus.

Lorsque vous avez terminé, assurez-vous de faire écho à 0 dans le fichier d'activation (et dans le fichier tracing_enabled si vous deviez l'activer également) pour désactiver le traçage afin que vous ne subissiez pas la perte de performance consistant à poursuivre le traçage.

Jonathan Kamens
la source
C'était une application de sauvegarde créant de nombreuses montres inotify, et la solution proposée dans la réponse acceptée a permis d'identifier le coupable. Cependant, je n’étais pas encore familiarisé avec le traçage des appels système que vous avez démontré ici. Très cool. Merci pour l'information!
larsks
3
Êtes-vous sûr qu'il s'agit de '/ sys / kernel / debug / tracing / tracing_enabled'? Sur mon système, il semble que le chemin correct soit '/ sys / kernel / debug / tracing / tracing_on' ...
Kartoch
Il n'y a pas de / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / enable ni sys / kernel / debug / tracing / tracing_enabled sur Gentoo Linux, mais il existe / sys / kernel / debug / tracing / tracing / tracing_enabled . Pourquoi donc?
mardi
Comme @Kartoch implique, vous devez faire echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_onsur les distributions modernes (Ubuntu 18.04.2 LTS).
oligofren
Il ne suffisait pas de faire les commandes pour moi, je devais aussi faire: `cd / sys / kernel / debug / tracing /; fonction echo> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren
8

Comme @Jonathan Kamens l'a dit, vous êtes probablement à court de montres. J'ai un script premade , inotify-consumersque les listes pour vous:

$ 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

Vous voyez rapidement ici pourquoi la limite par défaut de 8 000 observateurs est trop faible sur une machine de développement, car seule l'instance WebStorm dépasse rapidement cette limite lorsqu'un node_modulesrépertoire contenant des milliers de dossiers est atteint. Ajouter un observateur Webpack pour garantir les problèmes ...

Il suffit de copier le contenu du script (ou le fichier sur GitHub) et de le placer quelque part dans votre $PATH, comme /usr/local/bin. Pour référence, le contenu principal du script est simplement ceci

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

Si vous vous demandez comment augmenter les limites, voici comment le rendre permanent:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
oligofren
la source
1
Beaucoup d'autres suggestions n'ont pas bien fonctionné pour moi, mais ce script a très bien fonctionné sur Fedora 29. Merci!
Richard S. Hall
6

J'ai rencontré ce problème et aucune de ces réponses ne vous donne la réponse suivante: "Combien de surveillances utilise actuellement chaque processus?" Les one-liners vous indiquent tous le nombre d' instances ouvertes, ce qui ne représente qu'une partie de l'histoire, et les informations de trace sont utiles uniquement pour voir les nouvelles alertes s'ouvrir.

TL; DR: vous obtiendrez un fichier avec une liste d' inotifyinstances ouvertes et le nombre de leurs surveillances , ainsi que les pids et les binaires qui les ont générées, triés par ordre décroissant en fonction du nombre de surveillances:

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

C'est une grosse boule de bazar, alors voici comment je suis arrivé là. Pour commencer, j’ai exécuté un tailfichier de test et jeté un coup d’œil au fd ouvert:

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

Donc, 4 est le fd que nous voulons étudier. Voyons ce qu'il y a dedans fdinfopour ça:

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

Cela ressemble à une entrée pour la montre en bas!

Essayons quelque chose avec plus de montres, cette fois avec l' inotifywaitutilitaire, en regardant ce qui se trouve dedans /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! Plus d'entrées! Nous devrions donc avoir six choses à ce moment- /tmplà:

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

Excellent. Mon nouveau inotifywaita une entrée dans sa fdliste (c'est ce que comptent les autres on-line ici), mais six entrées dans son fdinfofichier. Nous pouvons donc déterminer combien de montres un fd donné utilise pour un processus donné en consultant son fdinfofichier. Maintenant, associez-le à certains des éléments ci-dessus pour obtenir une liste des processus qui ont ouvert les surveillances de notification et les utiliser pour compter les entrées de chacun fdinfo. Ceci est similaire à ce qui est décrit ci-dessus, je vais donc vider le one-liner ici:

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

Il y a des choses épaisses ici, mais les bases sont celles que j'utilise awkpour construire un fdinfochemin à partir de la lsofsortie, en récupérant les numéros pid et fd, en supprimant le drapeau u / r / w de cette dernière. Ensuite, pour chaque fdinfochemin construit , je compte le nombre de inotifylignes et affiche le nombre et le pid.

Ce serait bien si j'avais quels processus ces pids représentent au même endroit, n'est-ce pas? J'ai pensé ainsi. Ainsi, dans un peu particulier en désordre, je me suis installé à appeler dirnamedeux fois sur le fdinfochemin pour obtenir paquet pour /proc/<pid>, en ajoutant /exeà, puis en cours d' exécution readlinksur ce pour obtenir le nom exe du processus. Insérez-le également ici, triez-le par nombre de montres et redirigez-le dans un fichier afin de le conserver en sécurité et nous obtiendrons:

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

En exécutant cela sans sudo pour afficher uniquement les processus que j'ai lancés ci-dessus, je reçois:

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

Parfait! Une liste des processus, des fd et du nombre de montres utilisées par chacun, ce qui est exactement ce dont j'avais besoin.

Cincodenada
la source
Lors de l'utilisation lsofà cette fin, je vous recommande d'utiliser les -nPindicateurs pour éviter les recherches inutiles de noms de ports et DNS inversés. Dans ce cas particulier, -bwil est également recommandé d’ ajouter pour éviter de bloquer les appels système. Cela dit, avec lsofmon temps de travail gobé de 3 secondes sur mon humble station de travail (dont 2 secondes dans le noyau), cette approche est intéressante à explorer mais ne convient hélas pas à la surveillance.
BertD
J'ai trouvé votre one-line extrêmement lent, mais une belle amélioration est possible, au prix de quelques pertes d'informations (nous verrons les observateurs par processus, plutôt que par descripteur de fichier): Générez d'abord un fichier intermédiaire: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-oensuitecat 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

Pour tracer les processus qui consomment inotify montres (pas d' instances) , vous pouvez utiliser la fonction ftrace de dynamique du noyau si elle est activée dans le noyau.

L'option de noyau dont vous avez besoin est CONFIG_DYNAMIC_FTRACE.

Montez d'abord le système de fichiers debugfs s'il n'est pas déjà monté.

mount -t debugfs nodev /sys/kernel/debug

Allez dans le sous- tracingrépertoire de ce répertoire debugfs

cd /sys/kernel/debug/tracing

Activer le traçage des appels de fonction

echo function > current_tracer

Filtrer uniquement les SyS_inotify_add_watchappels système

echo SyS_inotify_add_watch > set_ftrace_filter

Efface le tampon de l'anneau de trace s'il n'était pas vide

echo > trace

Activer le traçage s'il n'est pas déjà activé

echo 1 > tracing_on

Redémarrez le processus suspect (dans mon cas, c’était un crashplan, une application de sauvegarde)

Regardez l'inotify_watch en train de s'épuiser

wc -l trace
cat trace

Terminé

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

J'ai modifié le script présenté ci-dessus pour afficher la liste des processus consommant des ressources inotify :

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

Je pense qu'il y a un moyen de remplacer mon double sed .


Oui. Utilisez soit

cut -f 3 -d '/'   

ou

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

et vous ne recevrez que le pid.
Aussi, si vous ajoutez

2> /dev/null  

dans la recherche, vous vous débarrasserez de toutes les lignes d'erreur embêtantes lancées par find. Donc, cela fonctionnerait:

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