我想监视目录中的每个文件更改inotifywait
。inotifywait
应写入 FIFO 缓冲区,然后可以从容地读取该缓冲区。在尝试相对大量的事件时,我遇到了一些瓶颈,我想了解这些瓶颈。
变化总是由 引起的touch {0000..9999}testfile
。瓶颈在于无法捕获所有文件事件。
当我将inotifywait
的输出重定向到文件时,所有内容都会按应有的方式记录。
inotifywait -q -m ./
写入终端捕获大约 5000 到 8000 个文件的 CREATE、OPEN、ATTRIB、CLOSE。我猜“写入屏幕”速度不够快,无法实现非阻塞?
如果我通过管道传输到cat
( inotifywait... | cat | ... | cat
),我最终会在某个时刻得到所有它们。所以我猜管道是一种缓冲,但我不太明白它是如何工作的,甚至不明白要查找什么。有人可以解释一下吗?
我还使用了我在这里找到的“解决方案”。用作pv -q -B 1g
缓冲区(也buffer
)。
inotifywait -q -m ./ | BUFFER | \
while read line; do
# Do something with $line or ...
sleep 1
done
如何确保每个文件事件都能被处理?我有一种感觉,我关于 bash voodoo 的小游戏发现了一些更深层次的限制,我想在其中有更多的见解。
答案1
如果 的输出inotifywait -q -m ./
未重定向并且您在终端仿真器中运行它,则输出将转到 pty 设备。设备pty
是进程间通信的一种形式,有点像管道,但添加了促进类似终端交互的功能。
在那个 pty 的另一端“管道”,您的终端模拟器将读取inotifywait
写入的内容并将其呈现在屏幕上。进行渲染非常复杂,并且占用 CPU 时间。
如果您的终端模拟器清空该管道的速度比inotifywait
填充该管道的速度慢,则 pty管道会吃饱的。当它已满时,就像管道一样,写入过程会阻塞(write()
系统调用不会返回),直到内存中再次有可用空间。“管道”。
在我的 Linux 版本中,我发现如果我一次写入 1 个字节,我可以向 pty 设备写入 19457 个字节,而另一端在阻塞之前不会读取任何内容:
$ socat -u 'exec:dd bs=1 if=/dev/zero,pty' 'exec:sleep inf,nofork' &
[1] 1247815
$ pkill -USR1 -x dd
19458+0 records in
19457+0 records out
19457 bytes (19 kB, 19 KiB) copied, 14.7165 s, 1.3 kB/s
如果我一次写入 2 个字节,则为 19458 个字节;如果一次写入 256 个字节,则为 19712 个字节;如果我将终端置于原始模式或在发送的数据中包含换行符(因为它们会转换为 CRLF),则为不同的值。
无论如何,我不认为缓冲区大小是可定制的。
inotifywait
使用inotify用于检索事件列表的 API。在inotify(7)
手册页中,您会发现:
以下接口可用于限制 inotify 消耗的内核内存量:
/proc/sys/fs/inotify/max_queued_events
当应用程序调用 inotify_init(2) 来设置可以排队到相应 inotify 实例的事件数上限时,将使用此文件中的值。超过此限制的事件将被丢弃,但始终会生成 IN_Q_OVERFLOW 事件。
当标准输出inotifywait
被阻塞时write()
,它无法处理内核放入该队列的事件。如果该队列本身已满,则事件将被丢弃。
在我的系统上,
$ sysctl fs.inotify.max_queued_events
fs.inotify.max_queued_events = 16384
现在,当你这样做时:
inotifywait -q -m ./ | cat
这次,我们在inotifywait
和之间有一个管道,在您的终端模拟器cat
之间有一个 pty 。cat
管道具有比 ptys 更大的缓冲区(Linux 上默认为 64KiB,但可以使用 )在每个管道上将其提高到fs.pipe-max-size
sysctl 值(默认情况下为 1MiB)fcntl(fd, F_SETPIPE_SZ, newsize)
。
因此,在inotifywait
'swrite()
块之前,我们需要填充这两个缓冲区。另外,cat
还会在自己的读取缓冲区中读取一些数据,并等待自己写入。
对于| cat
添加的每个内容,您都会添加额外的缓冲空间(至少多 64KiB)。
使用pv -q -B 1g
,pv
将在内部缓冲数据。
这些cat
将比pv
终端模拟器更快地读取输入,因为它们需要做的工作少得多来处理它,但如果inotifywait
读取/解码/格式化事件的速度不够快,仍然可以删除一些事件。
为了最大限度地减少事件被丢弃的可能性,您可以:
- 增加
fs.inotify.max_queued_events
- 避免将
inotifywait
输出发送给速度较慢的消费者,或者添加足够的缓冲(如果这样做) - 调整
inotifywait
过滤器以仅选择您感兴趣的事件。 - 确保
inotifywait
其输出的消费者没有被给予低优先级(nonice
ing 他们)。