web-dev-qa-db-ja.com

名前付きパイプを読む:尾または猫?

私は使用してファイル記述子を作成しました

mkfifo fifo

このパイプに何かが書き込まれたらすぐに再利用したい(immediately)。使うべきか

tail -f fifo

または

while true; do cat fifo; done

彼らは同じことをしているようで、私はパフォーマンスの違いを測定できませんでした。ただし、システムがinotify(Busyboxなど)をサポートしていない場合、前者は

tail -f -s 0 fifo

しかし、これはCPUを100%使用します(テストしてください:mkfifo fifo && busybox tail -f -s 0 fifo & echo hi>fifo/fg 1でキャンセルして、 CtrlC)。それでは、while-true-catの方が信頼性の高いソリューションですか?

7
phil294

あなたがするとき:

_cat fifo
_

他のプロセスがfifoをまだ書き込み用に開いていない場合、catopen()システムコールをブロックします。別のプロセスが書き込み用にファイルを開くと、パイプがインスタンス化され、open()が返されます。 catread()をループで呼び出し、read()は他のプロセスがパイプにデータを書き込むまでブロックします。

catは、他のすべての書き込みプロセスがファイル記述子をfifoにクローズしたときに、ファイルの終わり(eof)を参照します。その時点でcatは終了し、パイプは破棄されます¹。

その後、catを実行して、fifoに書き込まれる内容を読み取る必要があります(ただし、別のパイプインスタンスを介して)。

に:

_tail -f file
_

catと同様に、tailは、プロセスが書き込み用にファイルを開くのを待ちます。しかし、ここでは、最初からコピーする_-n +1_を指定しなかったため、tailは、最後の10行が何であるかを確認するためにeofまで待機する必要があるため、書き込みが終了するまで何も表示されません。閉まっている。

その後、tailはパイプへのfdを閉じません。つまり、パイプインスタンスは破棄されず、毎秒パイプからの読み取りを試行します(Linuxでは、inotifyを使用してポーリングを回避できます。 GNU tail do that there)。そのread()はeof(すぐに戻る)を返すため、_-s 0_(100 GNU tailは、他のプロセスがファイルを再度書き込み用に開くまで、1秒待つ代わりにread() sの間で待機しないことを意味します).

ここでは代わりに、catを使用することをお勧めしますが、パイプインスタンスがインスタンス化された後は常にパイプインスタンスがそのまま残るようにしてください。そのため、ほとんどのシステムでは、次のことができます。

_cat 0<> fifo # the 0 is needed for recent versions of ksh93 where the
             # default fd changed from 0 to 1 for the <> operator
_

catのstdinは読み取りと書き込みの両方で開かれます。つまり、catはeofを決して見ません(書き込みのためにfifoを開く他のプロセスがない場合でも、すぐにパイプをインスタンス化します)。

それが機能しないシステムでは、代わりに次のようにすることができます:

_cat < fifo 3> fifo
_

このように、他のプロセスがfifoを書き込み用に開くとすぐに、最初の読み取り専用open()が返されます。この時点で、シェルは開始前に書き込み専用open()を実行しますcat。パイプが再び破壊されるのを防ぎます。

まとめると、

  • _cat file_と比較すると、最初のラウンドの後で停止しません。
  • _tail -n +1 -f file_と比較して:最初のラウンドの後、毎秒役に立たないread()を実行しません。パイプの1つのインスタンスでeofが発生することはなく、最大1秒は発生しません。最初のプロセスがパイプを閉じた後、2番目のプロセスが書き込み用にパイプを開いたときの遅延。
  • _tail -f file_と比較されます。上記に加えて、何かを出力する前に最初のラウンドが終了するのを待つ必要はありません(最後の10行のみ)。
  • ループ内の_cat file_と比較すると、パイプインスタンスは1つしかありません。 ¹で言及されているレースウィンドウは回避されます。

thisこの時点で、eofを示す最後のread()catがパイプの読み取り終了を終了して閉じる間に、実際にはプロセスがfifoを開いて再度書き込むために(およびまだ読み終わりがあるのでブロックされません)。次に、catが終了した後、別のプロセスがfifoを読み取り用に開く前に何かを書き込むと、SIGPIPEで強制終了されます。

6

別の解決策を提案させてください。パイプは、いくつかのプロセスが2番目の端に書き込む限り、読み取りに使用できます。したがって、バックグラウンド(または別の端末)で偽のcatを作成できます。たとえば、次のようにします。

mkfifo fifo
cat >fifo &
cat fifo

今、あなたは好きなだけFIFOに書き込むことができ、終了したら現在のcatを殺すだけで C-c、次にfgを使用して、最初にcatをバックグラウンドから取得し、最後に C-d それを止める。

2
jimmij