了解 inotifywait、管道和缓冲区

了解 inotifywait、管道和缓冲区

我想监视目录中的每个文件更改inotifywaitinotifywait应写入 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-sizesysctl 值(默认情况下为 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其输出的消费者没有被给予低优先级(no niceing 他们)。

相关内容