Ai đang tiêu thụ tài nguyên inotify của tôi?


49

Sau lần nâng cấp gần đây lên Fedora 15, tôi thấy rằng một số công cụ bị lỗi với các lỗi:

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

Đó không chỉ taillà vấn đề báo cáo với inotify. Có cách nào để thẩm vấn kernel để tìm ra quy trình hoặc quy trình nào đang tiêu thụ tài nguyên inotify không? Các sysctlcài đặt liên quan đến inotify hiện tại trông như thế này:

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

Câu trả lời:


39

Có vẻ như nếu quá trình tạo cá thể inotify thông qua inotify_init (), tệp kết quả đại diện cho filedescriptor trong hệ thống tập tin / Proc là một liên kết đến tệp 'anon_inode: inotify' (không tồn tại).

$ 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

Trừ khi tôi hiểu nhầm khái niệm, lệnh sau sẽ hiển thị cho bạn danh sách các quy trình (đại diện của chúng trong / Proc), được sắp xếp theo số lượng phiên bản inotify mà chúng sử dụng.

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

8
Tuyệt vời cảm ơn bạn! Tôi không biết về các nút inotify hiển thị trong / Proc. Đối với mục đích của tôi, lệnh có thể được đơn giản hóa để:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print
larsks

Tôi rất vui vì nó đã giúp. Và giải pháp của bạn với find -lname thực sự đẹp hơn tôi rất nhiều với vòng lặp và đường dẫn đọc.
Petr Uzel

3
Lưu ý rằng bạn cũng có thể hết đồng hồ (không phải trường hợp). Ví dụ, trên hệ thống của tôi, cung cấp số lượng thanh thiếu niên thấp, nhưng có hàng chục nghìn đồng hồ từ tìm kiếm trên máy tính để bàn của KDE. Thật tệ, không có cách nào dễ dàng hơn để kiểm tra số lượng đồng hồ / trường hợp đang sử dụng, vì hạt nhân biết rõ ...
derobert

Để hiển thị các dòng lệnh của các chương trình vi phạm:find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -exec sh -c 'cat $(dirname {})/../cmdline; echo ""' \; 2>/dev/null
Mark K Cowan

@derobert Tôi đã tạo một kịch bản để liệt kê các quy trình tiêu thụ người theo dõi, thường là những gì một người quan tâm. Xem câu trả lời của tôi dưới đây.
oligofren

25

Bạn có thể đang chạy ra khỏi đồng hồ inotify chứ không phải là trường hợp. Để tìm ra ai tạo ra nhiều đồng hồ:

  1. Làm echo 1 >> /sys/kernel/debug/tracing/events/syscalls/sys_exit_inotify_add_watch/enableđể cho phép theo dõi của đồng hồ thêm;
  2. Làm cat /sys/kernel/debug/tracing/tracing_enabledđể đảm bảo nó được đặt thành 1 và nếu không, hãy làm echo 1 >> /sys/kernel/debug/tracing/tracing_enabled;
  3. Khởi động lại các quy trình với các trường hợp inotify (được xác định như được mô tả trong câu trả lời của Petr Uzel) mà bạn nghi ngờ tạo ra nhiều đồng hồ; và
  4. Đọc tệp /sys/kernel/debug/tracing/traceđể xem có bao nhiêu đồng hồ được tạo và theo quy trình nào.

Khi bạn đã hoàn tất, hãy đảm bảo lặp lại 0 vào tệp kích hoạt (và tệp truy tìm nếu bạn cũng phải bật tính năng đó) để tắt theo dõi để bạn không phải chịu ảnh hưởng của việc tiếp tục theo dõi.


Đó là một ứng dụng sao lưu tạo ra rất nhiều đồng hồ inotify và giải pháp trong câu trả lời được chấp nhận đã giúp xác định thủ phạm. Tuy nhiên, trước đây tôi không quen với theo dõi cuộc gọi hệ thống mà bạn đã trình bày ở đây. Rất tuyệt. Cảm ơn vì thông tin!
larsks

2
bạn có chắc chắn đó là '/ sys / kernel / debug / traces / traces_enables' không? Trên hệ thống của tôi, dường như đường dẫn chính xác là '/ sys / kernel / debug / traces / traces_on' ...
Kartoch

Không có / sys / kernel / debug / tracing / sự kiện / syscalls / sys_exit_inotify_add_watch / kích hoạt hay / sys / kernel / debug / tracing / tracing_enabled trên Gentoo Linux, nhưng / sys / kernel / debug / tracing / tracing_enabled tồn tại. Tại sao vậy?
zeekvfu

Như @Kartoch ngụ ý, bạn cần thực hiện echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_ontrên các bản phân phối hiện đại (Ubuntu 18.04.2 LTS).
oligofren

Nó không đủ để thực hiện các lệnh cho tôi, tôi cũng cần phải làm: `cd / sys / kernel / debug / traces /; chức năng echo> current_tracer; echo SyS_inotify_add_watch> set_ftrace_filter`
oligofren

7

Như @Jonathan Kamens đã nói, có lẽ bạn đang hết đồng hồ. Tôi có một kịch bản tiền đề , inotify-consumersliệt kê điều này cho bạn:

$ 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

Ở đây bạn nhanh chóng thấy lý do tại sao giới hạn mặc định của người theo dõi 8K là quá ít trên máy phát triển, vì chỉ cần phiên bản WebStorm nhanh chóng tối đa hóa điều này khi gặp một node_modulesthư mục có hàng ngàn thư mục. Thêm trình theo dõi webpack để đảm bảo sự cố ...

Chỉ cần sao chép nội dung của kịch bản (hoặc các tập tin trên GitHub) và đặt nó ở đâu đó trong bạn $PATH, giống như /usr/local/bin. Để tham khảo, nội dung chính của kịch bản chỉ đơn giản là

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

Trong trường hợp bạn đang tự hỏi làm thế nào để tăng giới hạn, đây là cách làm cho nó vĩnh viễn:

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

1
Rất nhiều đề xuất khác không hoạt động tốt với tôi, nhưng kịch bản này hoạt động rất tốt trên Fedora 29. Cảm ơn!
Richard S. Hall

6

Tôi gặp phải vấn đề này và không có câu trả lời nào trong số này cho bạn câu trả lời là " mỗi quy trình hiện đang sử dụng bao nhiêu đồng hồ ?" Tất cả các lớp lót đều cung cấp cho bạn bao nhiêu trường hợp được mở, đó chỉ là một phần của câu chuyện và các công cụ theo dõi chỉ hữu ích khi thấy đồng hồ mới được mở.

TL; DR: Điều này sẽ giúp bạn có một tệp với danh sách các inotifytrường hợp mở và số lượng đồng hồ họ có, cùng với các pids và nhị phân sinh ra chúng, được sắp xếp theo thứ tự giảm dần theo số lượng xem:

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

Đó là một quả bóng lớn lộn xộn, vì vậy đây là cách tôi đến đó. Để bắt đầu, tôi đã chạy một tailtệp thử nghiệm và xem fd của nó đã mở:

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

Vì vậy, 4 là fd chúng tôi muốn điều tra. Chúng ta hãy xem những gì trong 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

Trông giống như một mục cho đồng hồ ở phía dưới!

Hãy thử một cái gì đó với nhiều đồng hồ hơn, lần này với inotifywaittiện ích, chỉ cần xem bất cứ thứ gì có trong /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! Thêm mục! Vì vậy, chúng ta nên có sáu điều /tmpsau đó:

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

Xuất sắc. Cái mới của tôi inotifywaitmột mục trong fddanh sách của nó (đó là những gì mà một lớp lót khác ở đây đang đếm), nhưng sáu mục trong fdinfotệp của nó . Vì vậy, chúng ta có thể tìm ra có bao nhiêu đồng hồ một fd nhất định cho một quy trình nhất định đang sử dụng bằng cách tham khảo fdinfotệp của nó . Bây giờ để kết hợp nó với một số ở trên để lấy danh sách các quy trình đã thông báo cho đồng hồ mở và sử dụng nó để đếm các mục trong mỗi mục fdinfo. Điều này tương tự như ở trên, vì vậy tôi sẽ chỉ đổ một lớp lót ở đây:

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

Có một số thứ dày ở đây, nhưng điều cơ bản là tôi sử dụng awkđể xây dựng một fdinfođường dẫn từ lsofđầu ra, lấy số pid và fd, tước cờ u / r / w từ cái sau. Sau đó, với mỗi fdinfođường dẫn được xây dựng , tôi đếm số lượng inotifydòng và xuất số đếm và pid.

Sẽ thật tuyệt nếu tôi có những quy trình mà các pids này thể hiện ở cùng một nơi, phải không? Tôi đã nghĩ như vậy. Vì vậy, trong một chút đặc biệt lộn xộn, tôi quyết định gọi dirnamehai lần trên fdinfođường dẫn để nhận gói /proc/<pid>, thêm /exevào nó, và sau đó chạy readlinktrên đó để lấy tên exe của quy trình. Cũng ném nó vào đó, sắp xếp nó theo số lượng đồng hồ và chuyển hướng nó đến một tệp để giữ an toàn và chúng tôi nhận được:

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

Chạy mà không cần sudo để chỉ hiển thị các quy trình tôi đã khởi chạy ở trên, tôi nhận được:

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

Hoàn hảo! Danh sách các quy trình, fd và số lượng đồng hồ mỗi chiếc đang sử dụng, đó chính xác là những gì tôi cần.


Khi sử dụng lsofcho mục đích này, tôi khuyên bạn nên sử dụng các -nPcờ để tránh việc tra cứu không cần thiết tên DNS và cổng ngược. Trong trường hợp cụ thể này, việc thêm vào -bwđể tránh khả năng chặn các tòa nhà chọc trời cũng được khuyến nghị. Điều đó nói rằng, với việc lsofngấu nghiến 3 giây thời gian trên đồng hồ treo tường trên máy trạm khiêm tốn của tôi (trong đó 2 giây được sử dụng trong kernel), phương pháp này rất phù hợp để khám phá nhưng than ôi không phù hợp cho mục đích giám sát.
BertD 15/03/18

Tôi thấy một lớp lót của bạn cực kỳ chậm, nhưng có một sự cải thiện tốt, với chi phí mất một số thông tin (chúng ta sẽ thấy người theo dõi trên mỗi quy trình, thay vì mỗi mô tả tệp): Trước tiên hãy tạo một tệp trung gian: lsof | awk '/a_inode/ { gsub(/[urw]$/,"",$4); print "/proc/"$2"/fdinfo/"$4; }' | sed 's/fdinfo.*//' | sort | uniq > uniq-osau đó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

Để theo dõi quá trình nào tiêu thụ đồng hồ inotify (không phải phiên bản), bạn có thể sử dụng tính năng ftrace động của kernel nếu nó được bật trong kernel của bạn.

Tùy chọn kernel bạn cần là CONFIG_DYNAMIC_FTRACE.

Đầu tiên gắn hệ thống tập tin gỡ lỗi nếu nó chưa được gắn kết.

mount -t debugfs nodev /sys/kernel/debug

Đi dưới tracingthư mục con của thư mục debugfs này

cd /sys/kernel/debug/tracing

Cho phép theo dõi các cuộc gọi chức năng

echo function > current_tracer

Chỉ lọc SyS_inotify_add_watchcác cuộc gọi hệ thống

echo SyS_inotify_add_watch > set_ftrace_filter

Xóa bộ đệm vòng theo dõi nếu nó không trống

echo > trace

Bật theo dõi nếu nó chưa được kích hoạt

echo 1 > tracing_on

Khởi động lại quá trình bị nghi ngờ (trong trường hợp của tôi đó là crashplan, một ứng dụng dự phòng)

Xem inotify_watch đang cạn kiệt

wc -l trace
cat trace

Làm xong


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

1

Tôi đã sửa đổi tập lệnh hiện diện ở trên để hiển thị danh sách các quy trình đang tiêu thụ tài nguyên inotify :

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

Tôi nghĩ rằng có một cách để thay thế sed đôi của tôi .


Đúng. Sử dụng

cut -f 3 -d '/'   

hoặc là

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

và bạn sẽ chỉ nhận được pid.
Ngoài ra, nếu bạn thêm

2> /dev/null  

trong phần tìm kiếm, bạn sẽ thoát khỏi mọi dòng lỗi phiền toái được tìm thấy. Vì vậy, điều này sẽ làm việc:

ps -p $(find /proc/*/fd/* -type l -lname 'anon_inode:inotify' -print 2> /dev/null | sed -e 's/^\/proc\/\([0-9]*\)\/.*/\1/')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.