Хто споживає мої іноціативні ресурси?


49

Після недавнього оновлення до Fedora 15 я виявив, що ряд інструментів не вдається з помилками в рядках:

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

Це не лише tailповідомлення про проблеми з інотифікацією. Чи є якийсь спосіб допитати ядро, щоб з’ясувати, який процес або процеси споживають ресурси, що ініціюють? Поточні налаштування, пов’язані з інновацією sysctl, виглядають так:

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

Відповіді:


39

Здається, що якщо процес створює примірник inotify через inotify_init (), отриманий файл, який представляє fileescriptor у файловій системі / 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), відсортований за кількістю застосованих екземплярів, які вони використовують.

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

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

Я радий, що це допомогло. І ваше рішення з find -lname дійсно набагато приємніше, ніж моє для циклу та readlink.
Петро Узель

3
Зауважте, що ви також можете бути поза годинниками (не екземплярами). Наприклад, у моїй системі, що дає низько підлітковий екземпляр, але є багато десятків тисяч годин від пошуку на робочому столі KDE. Це занадто погано, що немає простого способу перевірити, скільки годинників / примірників використовується, оскільки ядро ​​чітко знає ...
derobert

Щоб показати командні рядки програм-порушників:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Марк К Кован

@derobert Я створив сценарій для переліку процесів, що споживають спостерігачів, про що, як правило, хвилює один. Дивіться мою відповідь нижче.
олігофрен

25

Напевно, у вас закінчується неізольований годинник, а не екземпляри. Щоб дізнатися, хто створює багато годин:

  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. Перезапустіть процеси з ініціативними екземплярами (визначеними, як описано у відповіді Петра Узела), на які ви підозрюєте, що створили багато годин; і
  4. Прочитайте файл, /sys/kernel/debug/tracing/traceщоб переглянути, скільки годин створено та за допомогою яких процесів.

Коли ви закінчите, переконайтесь, що відзвучить 0 у файлі увімкнення (і у файлі tracing_enabled, якщо вам також потрібно було це ввімкнути), щоб вимкнути трасування, щоб ви не зазнали хіба на продуктивність, щоб продовжувати відстежувати.


Це було резервне додаток, яке створило безліч інотифікованих годин, і рішення у прийнятій відповіді допомогло визначити винуватця. Однак я раніше не був знайомий із системою відстеження системних викликів, яку ви продемонстрували тут. Дуже круто. Спасибі за інформацію!
larsks

2
Ви впевнені, що це "/ 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 nor / 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).
олігофрен

Це було недостатньо для виконання команд для мене, я також повинен був зробити: `cd / sys / kernel / debug / tracing /; ехо-функція> current_tracer; відлуння sys_inotify_add_watch> set_ftrace_filter`
oligofren

7

Як сказав @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примірників та кількість годин, які вони мають, а також підручники та бінарні файли, які їх породили, відсортовані у порядку зменшення за кількістю перегляду:

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

Запустивши це без судо, щоб просто показати свої процеси, які я запустив вище, я отримую:

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.

Спочатку змонтуйте файлову систему налагодження, якщо вона ще не встановлена.

mount -t debugfs nodev /sys/kernel/debug

Перейдіть у tracingпідкаталог цього каталогу налагоджень

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

Перезапустіть підозрюваний процес (у моєму випадку це було збоєм, резервна програма)

Слідкуйте за тим, як 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

Я змінив присутній вище сценарій, щоб показати список процесів, які споживають ресурси, що ініціюють :

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

Я думаю, що є спосіб замінити мій подвійний sed .


Так. Використовуйте будь-який

cut -f 3 -d '/'   

або

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

і ти отримаєш лише під.
Також, якщо додати

2> /dev/null  

у знахідці ви позбудетесь від будь-яких прикріх ліній помилок, кинутих знахідкою. Отже, це спрацювало б:

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.