Quem está consumindo meus recursos de inotificação?

49

Após uma atualização recente para o Fedora 15, estou descobrindo que várias ferramentas estão falhando com erros nas seguintes linhas:

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

Também não tailestá relatando problemas com o inotify. Existe alguma maneira de interrogar o kernel para descobrir qual processo ou processos estão consumindo os recursos de inotificação? As sysctlconfigurações atuais relacionadas ao inotify ficam assim:

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

Respostas:

39

Parece que, se o processo cria uma instância inotify via inotify_init (), o arquivo resultante que representa o editor de arquivo no sistema de arquivos / proc é um link simbólico para o arquivo 'anon_inode: inotify' (não existente).

$ 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 eu tenha entendido errado o conceito, o comando a seguir deve mostrar uma lista de processos (sua representação em / proc), classificados pelo número de instâncias de inotificação que eles usam.

for foo in /proc/*/fd/*; do readlink -f $foo; done | grep inotify | sort | uniq -c | sort -nr
Petr Uzel
fonte
8
Excelente, obrigado! Eu não sabia sobre os inodes inotify aparecendo em / proc. Para os meus propósitos, o comando pode ser simplificada para isso:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks
Estou feliz que ajudou. E sua solução com find -lname é realmente muito melhor do que a minha com loop for e readlink.
precisa
3
Observe que você também pode ficar sem relógios (não instâncias). Por exemplo, no meu sistema, isso fornece um número baixo de adolescentes para instâncias, mas existem muitas dezenas de milhares de relógios na pesquisa na área de trabalho do KDE. É muito ruim não há uma maneira mais fácil de verificar quantos relógios / casos estão em uso, desde que o kernel sabe claramente ...
derobert
Para mostrar as linhas de comando dos programas incorretos:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan
@derobert Eu criei um script para listar os processos que consomem observadores, que geralmente é o que interessa. Veja minha resposta abaixo.
oligofren 2/04
25

Provavelmente você está ficando sem relógios inotify em vez de instâncias. Para descobrir quem está criando muitos relógios:

  1. Faça echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enablepara ativar o rastreamento de adições de relógios;
  2. Faça cat /sys/kernel/debug/tracing/tracing_enabledpara garantir que esteja definido como 1 e, se não estiver, faça echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Reinicie os processos com instâncias inotify (determinadas conforme descrito na resposta de Petr Uzel) que você suspeita de criar muitos relógios; e
  4. Leia o arquivo /sys/kernel/debug/tracing/tracepara ver quantos relógios são criados e por quais processos.

Quando terminar, certifique-se de ecoar 0 no arquivo de habilitação (e o arquivo tracing_enabled, se você também tiver que habilitá-lo) para desativar o rastreio, para que você não sofra o impacto no desempenho de continuar rastreando.

Jonathan Kamens
fonte
Era um aplicativo de backup que cria muitos relógios inotify, e a solução na resposta aceita ajudou a identificar o culpado. No entanto, eu não estava familiarizado com o rastreamento de chamadas do sistema que você demonstrou aqui. Muito legal. Obrigado pela informação!
Larsks
2
você tem certeza de que é '/ sys / kernel / debug / tracing / tracing_enabled'? No meu sistema parece que o caminho correto é '/ sys / kernel / debug / rastreamento / tracing_on' ...
Kartoch
Não existe / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / enable nem / sys / kernel / debug / tracing / tracing_enabled no Gentoo Linux, mas existe / sys / kernel / debug / tracing / tracing_enabled . Por que é que?
Zeekvfu
Como o @Kartoch implica, você precisa fazer echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_onas distribuições modernas (Ubuntu 18.04.2 LTS).
oligofren 22/02
Não foi o suficiente para executar os comandos para mim, eu também precisei: `cd / sys / kernel / debug / tracing /; função eco> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren
7

Como disse Jonathan Kamens, você provavelmente está ficando sem relógios. Eu tenho um script de premade , inotify-consumers, que as listas isso para você:

$ 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

Aqui você vê rapidamente por que o limite padrão de observadores de 8K é muito pequeno em uma máquina de desenvolvimento, pois apenas a instância do WebStorm o maximiza rapidamente ao encontrar uma node_modulespasta com milhares de pastas. Adicione um observador de webpack para garantir problemas ...

Basta copiar o conteúdo do script (ou o arquivo no GitHub) e colocá-lo em algum lugar do seu $PATH, como /usr/local/bin. Para referência, o conteúdo principal do script é simplesmente este

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

Caso você esteja se perguntando como aumentar os limites, veja como torná-lo permanente:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
oligofren
fonte
1
Muitas outras sugestões não funcionaram bem para mim, mas esse script funcionou muito bem no Fedora 29. Obrigado!
Richard S. Hall
6

Encontrei esse problema e nenhuma dessas respostas fornece a resposta "quantos relógios cada processo está usando atualmente?" Todos os itens de uma linha fornecem quantas instâncias estão abertas, o que é apenas parte da história, e o material de rastreamento é útil apenas para ver novos relógios sendo abertos.

TL; DR: você receberá um arquivo com uma lista de inotifyinstâncias abertas e o número de relógios que eles têm, juntamente com os pids e binários que os geraram, classificados em ordem decrescente pela contagem de visualizações:

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

É uma grande bagunça, então aqui está como cheguei lá. Para começar, eu corri um tailarquivo de teste e observei os arquivos abertos:

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

Então, 4 é o valor que queremos investigar. Vamos ver o que há fdinfopara isso:

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 uma entrada para o relógio na parte inferior!

Vamos tentar algo com mais relógios, desta vez com o inotifywaitutilitário, apenas observando o que estiver em /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! Mais entradas! Portanto, devemos ter seis coisas /tmp:

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

Excelente. Meu novo inotifywaittem uma entrada em sua fdlista (que é o que os outros itens aqui estão contando), mas seis entradas em seu fdinfoarquivo. Assim, podemos descobrir quantos relógios um determinado fd para um determinado processo está usando consultando seu fdinfoarquivo. Agora, junte-o a algumas das opções acima para obter uma lista de processos que notificaram a abertura de relógios e use isso para contar as entradas em cada um fdinfo. Isso é semelhante ao acima, então eu vou despejar o one-liner aqui:

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

Há algumas coisas grossas aqui, mas o básico é que eu uso awkpara criar um fdinfocaminho a partir da lsofsaída, pegando o número pid e fd, retirando a bandeira u / r / w do último. Então, para cada fdinfocaminho construído , conto o número de inotifylinhas e produzo a contagem e o pid.

Seria bom se eu tivesse quais processos esses pids representam no mesmo lugar, certo? Eu pensei assim. Assim, em um pouco particularmente confuso, eu estabeleci-me em chamar dirnameduas vezes no fdinfocaminho para chegar pack para /proc/<pid>, adicionando /exea ele, e depois correndo readlinkem que para obter o nome exe do processo. Jogue isso lá também, classifique-o pelo número de relógios e redirecione-o para um arquivo para proteção e obteremos:

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

Executando isso sem o sudo para apenas mostrar meus processos que iniciei acima, recebo:

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

Perfeito! Uma lista de processos, fd's e quantos relógios cada um está usando, exatamente o que eu precisava.

cincodenada
fonte
Ao usar lsofpara esse fim, recomendo o uso dos -nPsinalizadores para evitar pesquisas desnecessárias de nomes de porta e DNS reversos. Nesse caso em particular, -bwtambém é recomendável adicionar para evitar o bloqueio potencial de syscalls. Dito isso, com o lsofconsumo de 3 segundos do relógio de parede na minha humilde estação de trabalho (dos quais 2 segundos são gastos no kernel), essa abordagem é boa para a exploração, mas é inadequada para fins de monitoramento.
22418 BertD
Eu achei o seu one-liner extremamente lento, mas há uma boa melhoria possível, com o custo de algumas perdas de informações (veremos observadores por processo, em vez de por descritor de arquivo): Primeiro gere um arquivo intermediário: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-oentãocat 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 17/07
5

Para rastrear quais processos consomem inotify watches (não instâncias), você pode usar o recurso ftrace dinâmico do kernel, se estiver ativado no kernel.

A opção do kernel que você precisa é CONFIG_DYNAMIC_FTRACE.

Primeiro monte o sistema de arquivos debugfs se ele ainda não estiver montado.

mount -t debugfs nodev /sys/kernel/debug

Vá sob o tracingsubdiretório deste diretório debugfs

cd /sys/kernel/debug/tracing

Ativar rastreamento de chamadas de função

echo function > current_tracer

Filtrar apenas SyS_inotify_add_watchchamadas do sistema

echo SyS_inotify_add_watch > set_ftrace_filter

Limpe o buffer do anel de rastreamento se não estiver vazio

echo > trace

Ativar rastreio se ainda não estiver ativado

echo 1 > tracing_on

Reinicie o processo suspeito (no meu caso, era um plano de falha, um aplicativo de backup)

Assista ao inotify_watch sendo esgotado

wc -l trace
cat trace

Feito

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

Eu modifiquei o script presente acima para mostrar a lista de processos que estão consumindo inotify recursos:

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

Eu acho que existe uma maneira de substituir o meu sed duplo .


Sim. Use um

cut -f 3 -d '/'   

ou

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

e você só receberá o pid.
Além disso, se você adicionar

2> /dev/null  

na descoberta, você se livra de quaisquer linhas de erro traquinas lançadas pela descoberta. Então, isso funcionaria:

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