Siapa yang mengkonsumsi sumber daya yang tidak saya miliki?


49

Setelah upgrade ke Fedora 15 baru-baru ini, saya menemukan bahwa sejumlah alat gagal dengan kesalahan di sepanjang baris:

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

Bukan hanya tailitu yang melaporkan masalah dengan notifikasi, juga. Apakah ada cara untuk menginterogasi kernel untuk mengetahui proses atau proses apa yang menghabiskan sumber daya yang tidak diotifikasi? sysctlPengaturan yang terkait dengan inotify saat ini terlihat seperti ini:

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

Jawaban:


39

Tampaknya jika proses membuat instance inotify melalui inotify_init (), file yang dihasilkan yang mewakili file yang diarsipkan di sistem file / proc adalah symlink ke (tidak ada) file '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

Kecuali saya salah paham konsep, perintah berikut ini akan menunjukkan kepada Anda daftar proses (perwakilan mereka di / proc), diurutkan berdasarkan jumlah contoh tidak sah yang mereka gunakan.

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

8
Luar biasa terima kasih! Saya tidak tahu tentang inotify inode yang muncul di / proc. Untuk tujuan saya, perintah dapat disederhanakan untuk ini:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks

Saya senang itu membantu. Dan solusi Anda dengan find -lname memang jauh lebih bagus daripada milik saya dengan for loop dan readlink.
Petr Uzel

3
Perhatikan bahwa Anda juga bisa keluar dari jam tangan (bukan instance) Misalnya, pada sistem saya, itu memberikan jumlah kejadian remaja yang rendah, tetapi ada puluhan ribu jam tangan dari pencarian desktop KDE. Sayang sekali tidak ada cara yang lebih mudah untuk memeriksa berapa banyak arloji / instance sedang digunakan, karena kernel jelas tahu ...
derobert

Untuk menampilkan baris perintah dari program yang menyinggung:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan

@derobert Saya membuat skrip untuk membuat daftar proses yang memakan pengamat, yang biasanya menjadi perhatian orang. Lihat jawaban saya di bawah ini.
oligofren

25

Anda mungkin kehabisan jam tangan yang tidak sah dan bukan kejadian. Untuk mengetahui siapa yang membuat banyak jam tangan:

  1. Lakukan echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enableuntuk mengaktifkan pelacakan jam tangan;
  2. Lakukan cat /sys/kernel/debug/tracing/tracing_enableduntuk memastikan itu diatur ke 1 dan jika tidak dilakukan echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Mulai ulang proses dengan contoh yang tidak sah (ditentukan seperti yang dijelaskan dalam jawaban Petr Uzel) yang Anda curigai membuat banyak jam tangan; dan
  4. Baca file /sys/kernel/debug/tracing/traceuntuk menonton berapa banyak jam tangan yang dibuat dan proses apa.

Setelah selesai, pastikan untuk menggema 0 ke dalam file aktifkan (dan file tracing_enabled jika Anda juga harus mengaktifkannya) untuk mematikan pelacakan sehingga Anda tidak akan terkena dampak kinerja terus melacak.


Itu adalah aplikasi cadangan yang membuat banyak jam tangan tidak berlaku, dan solusi dalam jawaban yang diterima membantu mengidentifikasi pelakunya. Namun, saya sebelumnya tidak terbiasa dengan pelacakan panggilan sistem yang telah Anda contohkan di sini. Sangat keren. Terima kasih untuk informasi!
larsks

2
apakah Anda yakin itu '/ sys / kernel / debug / tracing / tracing_enabled'? Pada sistem saya, sepertinya jalan yang benar adalah '/ sys / kernel / debug / tracing / tracing_on' ...
Kartoch

Tidak ada / sys / kernel / debug / tracing / events / syscalls / sys_exit_inotify_add_watch / aktifkan juga / sys / kernel / debug / tracing / tracing_enabled di Gentoo Linux, tetapi / sys / kernel / debug / tracing / tracing_enabled ada. Mengapa demikian?
zeekvfu

Seperti yang disiratkan @Kartoch, Anda perlu melakukannya echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_ondi distro modern (Ubuntu 18.04.2 LTS).
oligofren

Itu tidak cukup untuk melakukan perintah untuk saya, saya juga perlu melakukan: `cd / sys / kernel / debug / tracing /; fungsi gema> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren

7

Seperti yang dikatakan @Jonathan Kamens, Anda mungkin kehabisan arloji. Saya memiliki naskah premade , inotify-consumersbahwa daftar ini untuk Anda:

$ 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

Di sini Anda dengan cepat melihat mengapa batas default 8K pengamat terlalu sedikit pada mesin pengembangan, karena hanya contoh WebStorm cepat maxes ini ketika menemukan node_modulesfolder dengan ribuan folder. Tambahkan pengamat webpack untuk menjamin masalah ...

Cukup salin isi skrip (atau file di GitHub) dan letakkan di suatu tempat di $PATH, seperti /usr/local/bin. Sebagai referensi, konten utama skrip hanyalah ini

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

Jika Anda bertanya-tanya bagaimana cara meningkatkan batas, berikut ini cara membuatnya permanen:

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

1
Banyak saran lain tidak bekerja dengan baik untuk saya, tetapi skrip ini bekerja dengan baik pada Fedora 29. Terima kasih!
Richard S. Hall

6

Saya mengalami masalah ini, dan tidak ada jawaban yang memberi Anda jawaban "berapa banyak jam tangan yang digunakan setiap proses saat ini?" The one-liners semua memberi Anda berapa banyak contoh terbuka, yang hanya bagian dari cerita, dan hal-hal jejak hanya berguna untuk melihat jam tangan baru dibuka.

TL; DR: Ini akan memberi Anda file dengan daftar inotifyinstance terbuka dan jumlah jam tangan yang dimilikinya, bersama dengan id dan binari yang memunculkannya, diurutkan dalam urutan menurun berdasarkan jumlah jam:

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

Itu adalah kekacauan besar, jadi inilah cara saya sampai di sana. Untuk memulai, saya menjalankan tailfile tes, dan melihat fd dibuka:

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

Jadi, 4 adalah fd yang ingin kita selidiki. Mari kita lihat apa untungnya 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

Itu terlihat seperti entri untuk arloji di bagian bawah!

Mari kita coba sesuatu dengan lebih banyak jam tangan, kali ini dengan inotifywaitutilitas, hanya menonton apa pun yang ada di /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! Lebih banyak entri! Jadi kita harus memiliki enam hal di /tmpdalamnya:

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

Luar biasa. Baru saya inotifywaitmemiliki satu entri dalam fddaftar (yang menghitung satu-liners lain di sini), tetapi enam entri dalam fdinfofile -nya . Jadi kita bisa mencari tahu berapa banyak jam tangan yang diberikan fd untuk proses tertentu menggunakan dengan berkonsultasi fdinfofile -nya . Sekarang untuk menggabungkannya dengan beberapa hal di atas untuk mengambil daftar proses yang memberitahukan jam tangan terbuka dan menggunakannya untuk menghitung entri di masing-masing fdinfo. Ini mirip dengan di atas, jadi saya hanya akan membuang satu-liner di sini:

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

Ada beberapa hal yang tebal di sini, tetapi dasar-dasarnya adalah yang saya gunakan awkuntuk membangun fdinfojalur dari lsofoutput, meraih nomor pid dan fd, melepas bendera u / r / w dari yang terakhir. Kemudian untuk setiap fdinfojalur yang dibangun , saya menghitung jumlah inotifygaris dan menampilkan jumlah dan pid.

Akan lebih baik jika saya memiliki proses apa yang disajikan oleh pid ini di tempat yang sama, kan? Saya pikir juga begitu. Jadi, dalam sedikit sangat berantakan, aku menetap di menelepon dirnamedua kali pada fdinfojalan untuk mendapatkan paket ke /proc/<pid>, menambahkan /exeuntuk itu, dan kemudian berjalan readlinkpada yang untuk mendapatkan nama exe proses. Lempar ke sana juga, urutkan berdasarkan jumlah jam tangan, dan arahkan ke file untuk disimpan dengan aman dan kami mendapatkan:

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

Menjalankan itu tanpa sudo untuk hanya menunjukkan proses yang saya luncurkan di atas, saya mendapatkan:

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

Sempurna! Daftar proses, fd, dan berapa banyak jam tangan masing-masing menggunakan, yang persis apa yang saya butuhkan.


Saat menggunakan lsofuntuk tujuan ini, saya akan merekomendasikan menggunakan -nPflag untuk menghindari pencarian yang tidak perlu dari DNS terbalik dan nama port. Dalam kasus khusus ini, menambahkan -bwuntuk menghindari kemungkinan memblokir syscalls juga dianjurkan. Yang mengatakan, dengan lsofmenelan 3 detik waktu jam dinding di workstation saya yang sederhana (yang menghabiskan 2 detik di kernel), pendekatan ini bagus untuk eksplorasi tetapi sayangnya tidak cocok untuk tujuan pemantauan.
BertD

Saya menemukan one-liner Anda menjadi sangat lambat, tetapi ada peningkatan yang bagus mungkin, dengan mengorbankan beberapa info (kita akan melihat pengamat per proses, daripada per deskriptor file): Pertama menghasilkan file perantara: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-okemudiancat 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

Untuk melacak proses mana yang menggunakan jam tangan tidak sah (bukan instance) Anda dapat menggunakan fitur ftrace dinamis dari kernel jika diaktifkan di kernel Anda.

Opsi kernel yang Anda butuhkan adalah CONFIG_DYNAMIC_FTRACE.

Pertama-tama pasang sistem file debugfs jika belum di-mount.

mount -t debugfs nodev /sys/kernel/debug

Pergi di bawah tracingsubdirektori dari direktori debugfs ini

cd /sys/kernel/debug/tracing

Aktifkan pelacakan panggilan fungsi

echo function > current_tracer

Saring hanya SyS_inotify_add_watchpanggilan sistem

echo SyS_inotify_add_watch > set_ftrace_filter

Bersihkan buffer cincin jejak jika tidak kosong

echo > trace

Aktifkan penelusuran jika belum diaktifkan

echo 1 > tracing_on

Mulai ulang proses yang dicurigai (dalam kasus saya ini adalah crashplan, aplikasi cadangan)

Tonton inotify_watch habis

wc -l trace
cat trace

Selesai


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

1

Saya telah memodifikasi skrip yang ada di atas untuk menunjukkan daftar proses yang memakan sumber daya tidak sah :

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

Saya pikir ada cara untuk mengganti sed ganda saya .


Iya. Gunakan salah satunya

cut -f 3 -d '/'   

atau

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

dan Anda hanya akan mendapatkan pid.
Juga, jika Anda menambahkan

2> /dev/null  

di find, Anda akan menghilangkan garis kesalahan sial yang dilemparkan oleh find. Jadi ini akan berhasil:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.