Chi sta consumando le mie risorse inotify?


49

Dopo un recente aggiornamento a Fedora 15, sto scoprendo che numerosi strumenti non funzionano con errori lungo la linea di:

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

Non è solo tailquello che sta segnalando problemi con inotify. Esiste un modo per interrogare il kernel per scoprire quale processo o quali processi stanno consumando le risorse di inotify? Le attuali sysctlimpostazioni relative a inotify sono simili alle seguenti:

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

Risposte:


39

Sembra che se il processo crea un'istanza inotify tramite inotify_init (), il file risultante che rappresenta filedescriptor nel filesystem / proc è un link simbolico al file 'anon_inode: inotify' (non esistente).

$ 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

A meno che non abbia frainteso il concetto, il seguente comando dovrebbe mostrarti un elenco di processi (la loro rappresentazione in / proc), ordinati per numero di istanze inotify che usano.

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

8
Eccellente grazie! Non sapevo degli inode di inotify mostrati in / proc. Per i miei scopi, il comando può essere semplificato in questo modo:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks

Sono contento che abbia aiutato. E la tua soluzione con find -lname è davvero molto più bella della mia con for loop e readlink.
Petr Uzel,

3
Nota che potresti anche essere fuori dagli orologi (non dalle istanze). Ad esempio, sul mio sistema, ciò fornisce un numero basso di casi di adolescenti, ma ci sono molte decine di migliaia di orologi dalla ricerca desktop di KDE. È un
peccato che

Per mostrare le linee di comando dei programmi offensivi:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan,

@derobert Ho creato uno script per elencare i processi che consumano osservatori, che di solito è ciò di cui ci si preoccupa. Vedi la mia risposta qui sotto.
oligofren

25

Probabilmente stai esaurendo gli orologi inotify anziché le istanze. Per scoprire chi sta creando molti orologi:

  1. Fare echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enableper abilitare la traccia degli orologi aggiunti;
  2. Fare cat /sys/kernel/debug/tracing/tracing_enabledper assicurarsi che sia impostato su 1 e in caso contrario echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Riavvia i processi con istanze inotify (determinate come descritto nella risposta di Petr Uzel) che sospetti di creare molti orologi; e
  4. Leggi il file /sys/kernel/debug/tracing/traceper vedere quanti orologi vengono creati e con quali processi.

Al termine, assicurati di inserire l'eco 0 nel file enable (e nel file tracing_enabled se dovessi abilitare anche quello) per disattivare la traccia in modo da non incorrere nel colpo di prestazione della continuazione della traccia.


Era un'applicazione di backup che creava molti orologi inotify e la soluzione nella risposta accettata ha aiutato a identificare il colpevole. Tuttavia, in precedenza non avevo familiarità con la traccia delle chiamate di sistema che hai dimostrato qui. Molto bello. Grazie per l'informazione!
Larks

2
sei sicuro che sia '/ sys / kernel / debug / tracing / tracing_enabled'? Sul mio sistema sembra che il percorso corretto sia '/ sys / kernel / debug / tracing / tracing_on' ...
Kartoch,

Non esiste / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / enable/ sys / kernel / debug / tracing / tracing_enabled su Gentoo Linux, ma esiste / sys / kernel / debug / tracing / tracing_enabled . Perché?
zeekvfu,

Come suggerisce @Kartoch, devi fare echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_onsu distro moderne (Ubuntu 18.04.2 LTS).
Oligofren,

Non era sufficiente eseguire i comandi per me, dovevo anche fare: `cd / sys / kernel / debug / tracing /; funzione echo> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren

7

Come ha detto @Jonathan Kamens, probabilmente stai esaurendo gli orologi. Ho un copione premade , inotify-consumers, che le liste di questo per voi:

$ 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

Qui puoi vedere rapidamente perché il limite predefinito di watcher 8K è troppo piccolo su una macchina di sviluppo, poiché solo l'istanza di WebStorm lo raggiunge rapidamente quando incontra una node_modulescartella con migliaia di cartelle. Aggiungi un osservatore webpack per garantire problemi ...

Basta copiare il contenuto dello script (o il file su GitHub) e metterlo da qualche parte nel tuo $PATH, come /usr/local/bin. Per riferimento, il contenuto principale dello script è semplicemente questo

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

Nel caso ti stia chiedendo come aumentare i limiti, ecco come renderlo permanente:

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

1
Molti altri suggerimenti non hanno funzionato bene per me, ma questo script ha funzionato benissimo su Fedora 29. Grazie!
Richard S. Hall,

6

Ho riscontrato questo problema e nessuna di queste risposte ti dà la risposta di "quanti orologi sta attualmente utilizzando ciascun processo?" Tutti i one-liner ti danno il numero di istanze aperte, che è solo una parte della storia, e la traccia è utile solo per vedere i nuovi orologi aperti.

TL; DR: questo ti porterà un file con un elenco di inotifyistanze aperte e il numero di orologi che hanno, insieme ai pid e ai binari che li hanno generati, ordinati in ordine decrescente in base al conteggio degli orologi:

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

È una grande palla di casino, quindi ecco come ci sono arrivato. Per iniziare, ho eseguito a tailsu un file di test e ho guardato i fd che si sono aperti:

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

Quindi, 4 è il fd che vogliamo investigare. Vediamo cosa c'è dentro fdinfoper quello:

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

Sembra una voce per l'orologio in basso!

Proviamo qualcosa con più orologi, questa volta con l' inotifywaitutilità, solo guardando tutto ciò che è in /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! Altre voci! Quindi dovremmo avere sei cose /tmpallora:

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

Eccellente. Il mio nuovo inotifywaitha una voce nella sua fdlista (che è ciò che contano le altre battute) qui, ma sei voci nel suo fdinfofile. Quindi possiamo capire quanti orologi sta usando un dato fd per un dato processo consultando il suo fdinfofile. Ora per metterlo insieme con alcuni dei precedenti per afferrare un elenco di processi che hanno aperto gli orologi di vigilanza e utilizzarlo per contare le voci in ciascuno fdinfo. Questo è simile a quello sopra, quindi scaricherò qui il one-liner:

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

Ci sono alcune cose spesse qui, ma le basi sono che io uso awkper creare un fdinfopercorso lsofdall'output, afferrando il numero pid e fd, togliendo la bandiera u / r / w da quest'ultimo. Quindi, per ogni fdinfopercorso costruito , conto il numero di inotifyrighe e produco il conteggio e il pid.

Sarebbe bello se avessi quali processi questi pid rappresentano nello stesso posto, giusto? Così ho pensato. Così, in un po 'particolarmente disordinato, ho optato per chiamare dirnamedue volte sul fdinfopercorso per arrivare a pacchetto /proc/<pid>, aggiungendo /exead esso, e quindi in esecuzione readlinksu che per ottenere il nome exe del processo. Aggiungilo anche lì, ordinalo per numero di orologi e reindirizzalo su un file per conservarlo in modo da ottenere:

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

Eseguendolo senza sudo per mostrare solo i miei processi che ho lanciato sopra, ottengo:

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

Perfetto! Un elenco di processi, FD e quanti orologi ciascuno sta usando, che è esattamente quello di cui avevo bisogno.


Quando si utilizza lsofa questo scopo, si consiglia di utilizzare i -nPflag per evitare ricerche non necessarie di nomi DNS e porte inverse. In questo caso particolare, -bwsi consiglia anche di aggiungere per evitare potenziali blocchi di chiamata. Detto questo, con il lsofdivorare 3 secondi di orologio da parete sulla mia umile workstation (di cui 2 secondi trascorsi nel kernel), questo approccio è utile per l'esplorazione ma purtroppo non adatto a scopi di monitoraggio.
BertD

Ho trovato il tuo one-liner estremamente lento, ma c'è un bel miglioramento possibile, al costo di una perdita di informazioni (vedremo gli osservatori per processo, piuttosto che per descrittore di file): prima genera un file intermedio: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-opoicat 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 il

5

Per tracciare i processi che consumano inotify orologi (non istanze) è possibile utilizzare la funzione dinamica ftrace del kernel se è abilitato nel vostro kernel.

L'opzione del kernel che ti serve è CONFIG_DYNAMIC_FTRACE.

Innanzitutto montare il filesystem debugfs se non è già montato.

mount -t debugfs nodev /sys/kernel/debug

Passare sotto la tracingsottodirectory di questa directory debugfs

cd /sys/kernel/debug/tracing

Abilita la traccia delle chiamate di funzione

echo function > current_tracer

Filtra solo SyS_inotify_add_watchle chiamate di sistema

echo SyS_inotify_add_watch > set_ftrace_filter

Cancella il buffer dell'anello di traccia se non era vuoto

echo > trace

Abilita traccia se non è già abilitato

echo 1 > tracing_on

Riavvia il processo sospetto (nel mio caso si è trattato di crashplan, un'applicazione di backup)

Guarda l'esaurimento di inotify_watch

wc -l trace
cat trace

Fatto


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

1

Ho modificato lo script presente sopra per mostrare l'elenco dei processi che stanno consumando risorse inotify :

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

Penso che ci sia un modo per sostituire la mia doppia sed .


Sì. Usa entrambi

cut -f 3 -d '/'   

o

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

e otterrai solo il pid.
Inoltre, se aggiungi

2> /dev/null  

nella ricerca, ti libererai di ogni fastidiosa linea di errore lanciata da find. Quindi questo funzionerebbe:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.