Кто потребляет мои ресурсы inotify?


49

После недавнего обновления до Fedora 15 я обнаружил, что некоторые инструменты перестали работать с ошибками, такими как:

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

Также tailсообщается о проблемах с inotify. Есть ли способ опросить ядро, чтобы выяснить, какой процесс или процессы потребляют ресурсы inotify? Текущие sysctlнастройки, связанные с inotify , выглядят так:

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

Ответы:


39

Кажется, что если процесс создает экземпляр inotify с помощью inotify_init (), результирующий файл, который представляет файловый дескриптор в файловой системе / proc, является символической ссылкой на (несуществующий) файл '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

Если я неправильно понял концепцию, следующая команда должна показать вам список процессов (их представление в / proc), отсортированный по количеству используемых экземпляров inotify.

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

Нахождение виновных

В комментариях ниже @markkcowan упомянул это:

$ find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null

8
Отлично, спасибо! Я не знал, какие inotify inodes отображаются в / proc. Для моих целей команду можно упростить до этого:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks

Я рад, что это помогло. И ваше решение с помощью команды find -lname действительно намного лучше, чем мое с помощью for и readlink.
Петр Узел

4
Обратите внимание, что вы также можете быть вне часов (не экземпляры). Например, в моей системе это дает небольшое количество экземпляров, но есть много десятков тысяч часов из поиска KDE на рабочем столе. Жаль, что нет более простого способа проверить, сколько часов / экземпляров используется, поскольку ядро ​​точно знает ...
derobert

Чтобы показать командные строки оскорбительных программ:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Марк К Коуэн

@derobert Я сделал скрипт для перечисления процессов, потребляющих наблюдателей, что обычно является тем, о чем кто-то заботится. Смотрите мой ответ ниже.
oligofren

25

Вероятно, у вас заканчиваются часы inotify, а не экземпляры. Чтобы узнать, кто создает много часов:

  1. Сделать, echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enableчтобы включить отслеживание добавления часов;
  2. Сделайте, cat /sys/kernel/debug/tracing/tracing_enabledчтобы убедиться, что он установлен в 1, и если это не так echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Перезапустите процессы с экземплярами inotify (определенными, как описано в ответе Петра Узела), которые вы подозреваете в создании большого количества часов; а также
  4. Прочтите файл, /sys/kernel/debug/tracing/traceчтобы узнать, сколько часов создано и какими процессами.

Когда вы закончите, убедитесь, что выведите 0 в файл включения (и в файл tracing_enabled, если вам нужно было также включить это), чтобы отключить трассировку, чтобы не понизить производительность при продолжении трассировки.


Это было приложение для резервного копирования, которое создавало множество часов inotify, и решение в принятом ответе помогло выявить виновника. Однако ранее я не был знаком с отслеживанием системных вызовов, которое вы продемонстрировали здесь. Очень круто. Спасибо за информацию!
Жаворонки

3
Вы уверены, что это '/ sys / kernel / debug / tracing / tracing_enabled'? В моей системе кажется, что правильный путь - «/ sys / kernel / debug / tracing / tracing_on» ...
Kartoch

В Gentoo Linux нет / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / enable, ни / sys / kernel / debug / tracing / tracing_enabled , но / sys / kernel / debug / tracing / tracing_enabled существует. Почему это?
zeekvfu

Как подразумевает @Kartoch, вам нужно работать echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_onна современных дистрибутивах (Ubuntu 18.04.2 LTS).
oligofren

Для меня было недостаточно выполнять команды, мне также нужно было сделать: `cd / sys / kernel / debug / tracing /; функция эха> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren

8

Как сказал @Jonathan Kamens, у вас, вероятно, заканчиваются часы. У меня есть Premade сценарий , inotify-consumers, что списки это для вас:

$ 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

Здесь вы быстро увидите, почему ограничение по умолчанию для 8K наблюдателей слишком мало на компьютере разработчика, так как только экземпляр WebStorm быстро достигает максимума при обнаружении node_modulesпапки с тысячами папок. Добавить наблюдателя веб-пакета, чтобы гарантировать проблемы ...

Просто скопируйте содержимое скрипта (или файл на GitHub) и поместите его где-нибудь в свой $PATH, например /usr/local/bin. Для справки, основное содержание сценария просто

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

Если вам интересно, как увеличить лимиты, вот как сделать его постоянным:

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

1
Многие другие предложения не сработали для меня, но этот скрипт отлично работал на Fedora 29. Спасибо!
Ричард С. Холл

6

Я столкнулся с этой проблемой, и ни один из этих ответов не дает ответа на вопрос "сколько часов каждый процесс использует в настоящее время?" Все строки указывают на то, сколько экземпляров открыто, что является лишь частью истории, а трассировка полезна только для того, чтобы увидеть, как открываются новые часы.

TL; DR: Это даст вам файл со списком открытых inotifyэкземпляров и количеством часов, которые они имеют, а также pids и двоичные файлы, которые их породили, отсортированные в порядке убывания по количеству часов:

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

Это большой беспорядок, вот как я туда попал. Для начала я запустил tailтестовый файл и посмотрел на открытые файлы:

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

Итак, 4 - это то, что мы хотим исследовать. Давайте посмотрим, что 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

Это выглядит как запись для часов внизу!

Давайте попробуем что-нибудь с большим количеством часов, на этот раз с inotifywaitутилитой, просто наблюдая за тем, что есть /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

Ага! Больше записей! Итак, у нас должно быть шесть вещей /tmp:

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

Отлично. Мой новый inotifywaitимеет одну запись в своем fdсписке (это то, что подсчитывают другие однострочники), но шесть записей в своем fdinfoфайле. Таким образом, мы можем выяснить, сколько часов использует данный fd для данного процесса, просмотрев его fdinfoфайл. Теперь, чтобы сложить его с некоторыми из вышеперечисленных, можно получить список процессов, у которых открыты часы уведомлений, и использовать их для подсчета записей в каждом fdinfo. Это похоже на вышеприведенное, поэтому я просто дам здесь однострочник:

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

Здесь есть некоторые толстые вещи, но основы в том, что я использую, awkчтобы построить fdinfoпуть из lsofвыходных данных, захватывая pid и число fd, убирая флаг u / r / w из последнего. Затем для каждого построенного fdinfoпути я подсчитываю количество inotifyстрок и выводю количество и pid.

Было бы неплохо, если бы у меня были процессы, которые эти пиды представляют в одном месте, правда? Я так и думал. Так, в частности , грязных немного, я остановился на вызов dirnameдважды на fdinfoпути , чтобы получить пакет к /proc/<pid>, добавив /exeк нему, а затем работает readlinkна том , что , чтобы получить имя еха процесса. Также добавьте это, отсортируйте по количеству часов и перенаправьте в файл для сохранности, и мы получим:

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

Запустив это без sudo, чтобы просто показать мои процессы, которые я запустил выше, я получаю:

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

Отлично! Список процессов, fd, и сколько часов каждый использует, что именно то, что мне нужно.


При использовании lsofдля этой цели я бы рекомендовал использовать -nPфлаги, чтобы избежать ненужных поисков обратных DNS и имен портов. В этом конкретном случае -bwтакже рекомендуется добавить, чтобы избежать потенциально блокирующих системных вызовов. Тем не менее, при lsofсожжении 3 секунд времени настенных часов на моей скромной рабочей станции (из которых 2 секунды тратятся в ядре), этот подход хорош для исследования, но увы не подходит для целей мониторинга.
BertD

Я обнаружил, что ваша однострочная работа очень медленная, но есть хорошее улучшение за счет некоторой потери информации (мы будем видеть наблюдателей за процессом, а не за дескриптором файла): сначала создайте промежуточный файл: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-oзатемcat 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

Чтобы отследить, какие процессы потребляют часы inotify (не экземпляры), вы можете использовать функцию динамического ftrace ядра, если она включена в вашем ядре.

Опция ядра вам нужна CONFIG_DYNAMIC_FTRACE.

Сначала смонтируйте файловую систему debugfs, если она еще не смонтирована.

mount -t debugfs nodev /sys/kernel/debug

Перейдите в tracingподкаталог этого каталога debugfs

cd /sys/kernel/debug/tracing

Включить отслеживание вызовов функций

echo function > current_tracer

Фильтровать только SyS_inotify_add_watchсистемные вызовы

echo SyS_inotify_add_watch > set_ftrace_filter

Очистить буфер трассировки, если он не был пуст

echo > trace

Включить трассировку, если она еще не включена

echo 1 > tracing_on

Перезапустите подозрительный процесс (в моем случае это был crashplan, приложение для резервного копирования)

Посмотрите, как исчерпаны inotify_watch

wc -l trace
cat trace

Готово


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

1

Я изменил скрипт, представленный выше, чтобы показать список процессов, которые потребляют ресурсы inotify :

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

Я думаю, что есть способ заменить мой двойной сед .


Да. Используйте либо

cut -f 3 -d '/'   

или же

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

и вы получите только пид.
Также, если вы добавите

2> /dev/null  

в поиске вы избавитесь от досадных ошибок, которые выдает команда find. Так что это будет работать:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.