inotifywait、パイプ、バッファについて学ぶ

inotifywait、パイプ、バッファについて学ぶ

ディレクトリ内のすべてのファイルの変更を監視したいと思いますinotifywaitinotifywaitFIFOバッファに書き込む必要があり、その後正常に読み取ることができます。比較的多数のイベントを試みている間、理解したいボトルネックがいくつか発生しました。

変化は常にによって発生します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桟橋についての私の小さなゲームがより深い限界を発見したという感じを受け、それについてより多くの洞察力を持っていたいと思います。

答え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ブロックwrite()の前にこれらの2つのバッファを埋める必要があります。また、cat一部のデータは、独自の読み取りバッファから読み取られ、書き込まれるのを待ちます。

コンテンツを追加するたびに、追加の| catバッファスペース(最小64KiB以上)を追加します。

使用すると、pv -q -B 1gデータpvは内部的にバッファリングされます。

これは、入力を処理するためにはるかに少ない作業を行う必要があるため、端末エミュレータよりも速く入力を読み込みますが、十分に速く読み込み、復号化、またはフォーマットcatできない場合は、一部のイベントを削除できます。pvinotifywait

イベントが削除される可能性を最小限に抑えるには、次のようにします。

  • 増加するfs.inotify.max_queued_events
  • 遅い消費者に出力を送信しない場合、inotifywaitまたはこの場合は十分なバッファリングを追加してください。
  • フィルタを調整して興味のあるinotifywaitイベントのみを選択してください。
  • inotifywait出力消費者に低い優先順位が与えられていないことをnice確認してください。

関連情報