¿Quién está consumiendo mis recursos de inotify?

49

Después de una actualización reciente a Fedora 15, descubro que varias herramientas están fallando con errores en la línea de:

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

No solo taileso es informar problemas con inotify, tampoco. ¿Hay alguna forma de interrogar al kernel para averiguar qué proceso o procesos están consumiendo los recursos de inotify? La sysctlconfiguración actual relacionada con inotify se ve así:

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

Respuestas:

39

Parece que si el proceso crea una instancia de inotify a través de inotify_init (), el archivo resultante que representa el descriptor de archivos en el sistema de archivos / proc es un enlace simbólico al archivo (no existente) '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

A menos que haya entendido mal el concepto, el siguiente comando debería mostrarle una lista de procesos (su representación en / proc), ordenados por el número de instancias de inotify que usan.

for foo in /proc/*/fd/*; do readlink -f $foo; done | grep inotify | sort | uniq -c | sort -nr
Petr Uzel
fuente
8
¡Excelente gracias! No sabía sobre los inodos de inotify que aparecen en / proc. Para mis propósitos, el comando se puede simplificar a esto:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks
Me alegra que haya ayudado. Y su solución con find -lname es mucho mejor que la mía para loop y readlink.
Petr Uzel
3
Tenga en cuenta que también podría quedarse sin relojes (no instancias). Por ejemplo, en mi sistema, eso da un número bajo de instancias de adolescentes, pero hay muchas decenas de miles de relojes en la búsqueda de escritorio de KDE. Es una pena que no hay una manera más fácil de comprobar cuántos relojes / instancias están en uso, ya que el núcleo tiene claro ...
Derobert
Para mostrar las líneas de comando de los programas ofensivos:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan
@derobert Hice un script para enumerar los procesos que consumen observadores, que generalmente es lo que a uno le importa. Vea mi respuesta a continuación.
oligofren
25

Probablemente se esté quedando sin relojes inotify en lugar de instancias. Para saber quién está creando muchos relojes:

  1. Hacer echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enablepara permitir el seguimiento de las adiciones de reloj;
  2. Haga cat /sys/kernel/debug/tracing/tracing_enabledpara asegurarse de que esté configurado en 1 y si no lo está echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Reinicie los procesos con instancias de inotify (determinadas como se describe en la respuesta de Petr Uzel) que sospecha que crea muchos relojes; y
  4. Lea el archivo /sys/kernel/debug/tracing/tracepara ver cuántos relojes se crean y por qué procesos.

Cuando haya terminado, asegúrese de hacer eco de 0 en el archivo de habilitación (y el archivo tracing_enabled si tenía que habilitar eso también) para desactivar el seguimiento para que no incurra en el impacto de rendimiento de continuar rastreando.

Jonathan Kamens
fuente
Era una aplicación de respaldo que creaba muchos relojes inotify, y la solución en la respuesta aceptada ayudó a identificar al culpable. Sin embargo, no estaba familiarizado con el seguimiento de llamadas del sistema que ha demostrado aquí. Muy genial. ¡Gracias por la información!
larsks
2
¿estás seguro de que es '/ sys / kernel / debug / tracing / tracing_enabled'? En mi sistema parece que la ruta correcta es '/ sys / kernel / debug / tracing / tracing_on' ...
Kartoch
No hay / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / enable ni / sys / kernel / debug / tracing / tracing_enabled en Gentoo Linux, pero / sys / kernel / debug / tracing / tracing_enabled existe. ¿Porqué es eso?
zeekvfu
Como @Kartoch implica, debes hacerlo echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_onen distribuciones modernas (Ubuntu 18.04.2 LTS).
oligofren
No fue suficiente hacer los comandos por mí, también tenía que hacer: `cd / sys / kernel / debug / tracing /; función echo> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren
7

Como dijo @Jonathan Kamens, probablemente te estés quedando sin relojes. Tengo un guión preparado de antemano , inotify-consumers, que las listas para el usuario:

$ 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

Aquí puede ver rápidamente por qué el límite predeterminado de los observadores de 8K es demasiado pequeño en una máquina de desarrollo, ya que solo la instancia de WebStorm maximiza rápidamente esto cuando encuentra una node_modulescarpeta con miles de carpetas. Agregue un monitor webpack para garantizar problemas ...

Simplemente copie el contenido del script (o el archivo en GitHub) y póngalo en algún lugar de su $PATH, como /usr/local/bin. Como referencia, el contenido principal del script es simplemente este

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

En caso de que se pregunte cómo aumentar los límites, aquí le mostramos cómo hacerlo permanente:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
oligofren
fuente
1
Muchas otras sugerencias no funcionaron bien para mí, pero este script funcionó muy bien en Fedora 29. ¡Gracias!
Richard S. Hall
6

Me encontré con este problema, y ​​ninguna de estas respuestas te da la respuesta de "¿cuántos relojes usa actualmente cada proceso?" Todas las frases te dan cuántas instancias están abiertas, lo cual es solo una parte de la historia, y las cosas de seguimiento solo son útiles para ver que se abren nuevos relojes.

TL; DR: Esto le dará un archivo con una lista de inotifyinstancias abiertas y la cantidad de relojes que tienen, junto con las imágenes y binarios que los generaron, ordenados en orden descendente por conteo de relojes:

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

Esa es una gran bola de desastre, así que así es como llegué allí. Para comenzar, ejecuté un tailarchivo de prueba y miré los archivos que abrió:

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

Entonces, 4 es el fd que queremos investigar. Veamos qué hay fdinfopara eso:

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

¡Parece una entrada para el reloj en la parte inferior!

Probemos algo con más relojes, esta vez con la inotifywaitutilidad, solo mirando lo que esté en /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

¡Ajá! Más entradas! Entonces deberíamos tener seis cosas /tmp:

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

Excelente. Mi nuevo inotifywaittiene una entrada en su fdlista (que es lo que cuentan las otras frases aquí), pero seis entradas en su fdinfoarchivo. Por lo tanto, podemos averiguar cuántos relojes está utilizando un fd determinado para un proceso determinado consultando su fdinfoarchivo. Ahora para unirlo con algunos de los anteriores para obtener una lista de procesos que tienen abiertos los relojes de notificación y usarlos para contar las entradas en cada uno fdinfo. Esto es similar a lo anterior, así que simplemente volcaré la línea aquí:

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

Hay algunas cosas gruesas aquí, pero lo básico es que utilizo awkpara construir una fdinforuta desde la lsofsalida, tomando el número pid y fd, quitando el indicador u / r / w de este último. Luego, para cada fdinforuta construida , cuento el número de inotifylíneas y genero el recuento y el pid.

Sería bueno si tuviera qué procesos representan estos pids en el mismo lugar, ¿verdad? Ya me lo imaginaba. Así, en un poco desordenado en particular, que se establecieron en llamar dirnamedos veces en el fdinfocamino para llegar al paquete /proc/<pid>, añadiendo /exea ella, y luego ejecutando readlinken que para obtener el nombre del exe del proceso. Agregue eso también, ordénelo por número de relojes y rediríjalo a un archivo para guardarlo y obtenemos:

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

Al ejecutar eso sin sudo para mostrar solo mis procesos que inicié anteriormente, obtengo:

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

¡Perfecto! Una lista de procesos, fd y cuántos relojes usa cada uno, que es exactamente lo que necesitaba.

cincodenada
fuente
Cuando lo use lsofpara este propósito, recomendaría usar las -nPbanderas para evitar búsquedas innecesarias de DNS inverso y nombres de puerto. En este caso particular, -bwtambién se recomienda agregar para evitar posibles bloqueos del sistema. Dicho esto, con lsofengullir 3 segundos de tiempo de reloj de pared en mi humilde estación de trabajo (de los cuales 2 segundos se gastan en el núcleo), este enfoque es bueno para la exploración, pero desafortunadamente no es adecuado para fines de monitoreo.
BertD
Descubrí que su one-liner es extremadamente lento, pero hay una buena mejora posible, a costa de alguna pérdida de información (veremos observadores por proceso, en lugar de por descriptor de archivo): Primero genere un archivo intermedio: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-oluegocat 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

Para rastrear qué procesos consumen relojes inotify (no instancias), puede usar la función de trazado dinámico del núcleo si está habilitado en su núcleo.

La opción de núcleo que necesita es CONFIG_DYNAMIC_FTRACE.

Primero monte el sistema de archivos debugfs si aún no está montado.

mount -t debugfs nodev /sys/kernel/debug

Vaya bajo el tracingsubdirectorio de este directorio debugfs

cd /sys/kernel/debug/tracing

Habilitar el rastreo de llamadas a funciones

echo function > current_tracer

Filtra solo SyS_inotify_add_watchllamadas del sistema

echo SyS_inotify_add_watch > set_ftrace_filter

Borre el búfer del anillo de rastreo si no estaba vacío

echo > trace

Habilite el seguimiento si aún no está habilitado

echo 1 > tracing_on

Reinicie el proceso sospechoso (en mi caso fue crashplan, una aplicación de respaldo)

Mira cómo se agota el inotify_watch

wc -l trace
cat trace

Hecho

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

He modificado el script presente en el cuadro anterior para mostrar la lista de procesos que consumen recursos de inotify :

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

Creo que hay una manera de reemplazar mi doble sed .


Si. Utilizar cualquiera

cut -f 3 -d '/'   

o

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

y solo obtendrás el pid.
Además, si agrega

2> /dev/null  

en el hallazgo, se librará de las molestas líneas de error que arroja find. Entonces esto funcionaría:

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