누가 inotify 리소스를 소비합니까?


49

최근 Fedora 15로 업그레이드 한 후 다음과 같은 여러 도구가 오류와 함께 실패하고 있음을 발견했습니다.

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

tailinotify와 관련된 문제를보고하는 것만 이 아닙니다 . 어떤 프로세스 또는 프로세스가 inotify 리소스를 소비하는지 알아 내기 위해 커널을 조사 할 수있는 방법이 있습니까? 현재 inotify 관련 sysctl설정은 다음과 같습니다.

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

답변:


39

프로세스가 inotify_init ()를 통해 inotify 인스턴스를 만드는 경우 / 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

개념을 잘못 이해하지 않은 경우 다음 명령은 사용하는 inotify 인스턴스 수를 기준으로 프로세스 목록 (/ proc로 표시)을 표시해야합니다.

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

8
훌륭 해요, 고맙습니다! / proc에 inotify inode가 나타나는지 몰랐습니다. 내 목적을 위해 명령을 다음과 같이 단순화 할 수 있습니다.find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks

도움이되어서 다행입니다. 그리고 find -lname을 사용하는 솔루션은 실제로 for 루프 및 readlink를 사용하는 것보다 훨씬 좋습니다.
Petr Uzel 2016 년

3
시계가 없을 수도 있습니다 (인스턴스 아님). 예를 들어, 내 시스템에서는 인스턴스 수가 적지 만 KDE의 데스크톱 검색에는 수만 개의 시계가 있습니다. 커널이 분명히 알고 있기 때문에 사용중인 시계 / 인스턴스 수를 확인하는 쉬운 방법이 너무 나쁩니다.
derobert

문제가되는 프로그램의 명령 줄을 표시하려면 :find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan

@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 인스턴스 (Petr Uzel의 답변에 설명 된대로 결정)로 프로세스를 다시 시작하십시오. 과
  4. /sys/kernel/debug/tracing/trace생성 된 시계 수와 프로세스 수를 보려면 파일 을 읽으십시오 .

완료되면 추적을 끄려면 0을 사용 파일 (및 사용하도록 설정해야하는 경우 tracing_enabled 파일)로 에코하여 추적을 계속해도 성능이 저하되지 않도록하십시오.


많은 inotify 시계를 생성하는 백업 응용 프로그램이었으며 허용 된 답변의 솔루션이 범인을 식별하는 데 도움이되었습니다. 그러나 이전에 여기서 시연 한 시스템 호출 추적에 익숙하지 않았습니다. 매우 시원합니다. 정보 주셔서 감사합니다!
larsks

2
'/ sys / kernel / debug / tracing / tracing_enabled'입니까? 내 시스템에서 올바른 경로는 '/ sys / kernel / debug / tracing / tracing_on'입니다.
Kartoch

젠투 리눅스 에는 / 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

7

@Jonathan Kamens가 말했듯이 아마도 시계가 부족할 수 있습니다. 나는이 미리 만들어진 스크립트 , 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

여기서는 node_modules수천 개의 폴더 가있는 폴더를 발견 할 때 WebStorm 인스턴스가이를 최대한 빨리 최대화하기 때문에 개발 시스템에서 8K 감시자의 기본 제한이 너무 적은 이유를 빠르게 확인할 수 있습니다 . 문제를 보장하기 위해 웹팩 감시자 추가 ...

스크립트의 내용 (또는 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에서 훌륭하게 작동했습니다. 감사합니다!
Richard S. Hall

6

나는이 문제에 부딪 쳤고,이 답변들 중 어느 것도 " 현재 각 프로세스가 얼마나 많은 시계를 사용하고 있습니까?" 하나의 라이너는 모두 열려있는 인스턴스 수를 제공 하며, 이는 이야기의 일부일 뿐이며, 추적 내용은 새 시계가 열리는 것을 보는 데만 유용합니다.

TL; DR : 열린 inotify인스턴스 목록과 가지고있는 시계 수와 함께 생성 된 pid 및 바이너리와 함께 시계 수별로 내림차순으로 정렬 된 파일을 가져옵니다.

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위해 테스트 파일을 실행하고 열린 fd를 보았습니다.

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

따라서 조사하고자하는 fd는 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그때 6 가지를 가져야합니다 .

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

우수한. 내 새로운 목록 inotifywait에는 하나의 항목 fd(여기서는 다른 하나의 라이너가 계산하는 항목)이 있지만 fdinfo파일 에는 6 개의 항목이 있습니다. 따라서 주어진 프로세스에 대해 주어진 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를 출력합니다.

이 pid가 같은 장소에서 어떤 프로세스를 나타내는 지 알면 좋을 것입니다. 나는 그렇게 생각했다. 그래서, 특히 지저분한 비트에, 나는 호출에 정착 dirname온 두 번 fdinfo에 팩을 얻을 수있는 경로 /proc/<pid>추가, /exe그것을 다음 실행 readlink 프로세스의 EXE 이름을 얻을 수 있습니다. 거기에 그것을 던져서 시계 수로 정렬하고 안전하게 보관하기 위해 파일로 리디렉션하면 다음과 같이됩니다.

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 초의 벽시계 시간을 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 watch (인스턴스 아님)를 소비하는지 추적하려면 커널에서 커널이 활성화 된 경우 커널의 동적 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

의심되는 프로세스를 다시 시작하십시오 (필자의 경우 충돌 계획, 백업 응용 프로그램 임)

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.*$'/''/`

더블 sed 를 교체 할 수있는 방법이 있다고 생각 합니다.


예. 둘 중 하나를 사용하십시오

cut -f 3 -d '/'   

또는

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

그리고 당신은 단지 pid를 얻을 것입니다.
또한 추가하면

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/')
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.