FIFO Special File
前置き
元々は[ffmpeg: 任意の画像加工コマンドで動画を加工する]内の文書だったが、長くなったので分けたもの。
基本動作
データが first-in first-out する特殊ファイル, named pipeとも呼ばれる。以降fifoと表記。
cat example.txt | moreの|vertical-lineによるパイプよりも複雑なことをするための道具の一つ。
fifo(7)
bash(1)/SHELL GRAMMAR/pipeline
open(2)/NOTES/FIFOs
fifoに対して書き込み側が"abc…"と書き込むと、読み込み側に"abc…"と出て来るイメージ。
だからパイプと比喩される。しかし、fifoはパイプそのものではない。
上記動作をするものがパイプ。
一方でパイプ関係を成立させるためのパスがfifoファイル。
fifoファイル自体はすなわちでパイプではない。後述のパイプの終了に関する仕様が理由。
fifoファイルはファイルシステム上にパスを持つが、データがストレージに保存されるわけではない。
見掛け上のファイルサイズは常に0 Byte。
利用する上で注意しなければいけないことはブロッキングとパイプの終了。
ブロッキング
fifoでは読み込み側と書き込み側の両方がopen操作をするまで、つまり両者が揃うまで、それぞれのopenは終わらない。
このことを指して「(条件が揃うまで)ブロックされる」や「(条件が揃うまで)制御は戻らない」などと呼ぶ。
ブロッキングはbashでfifoを扱う場合にも関係する。
次のbash表現はブロッキングが起きる可能性がある。
exec 110< "fifoA"
# fifoAをread-modeでopenして、110番のfile descriptorで管理するという意味
openの操作がある。そのため、別のプロセスがパイプのもう一方として"fifoA"をwrite-modeでopenするまで上記execは終わらない(制御は戻らない)。
bashのリダイレクト操作構文については別ページ[bashのFD(file descriptor)操作について]へ。
このブロッキングには例外(それと少し予測しにくい挙動)がある
O_NONBLOCKを指定してopenした場合はブロックされない。。
rw-mode(read and write mode)でのopenの際はブロックされない。
bashではO_NONBLOCK指定はできないため、関係がない。
bashでは<>less-than-sign,greater-than-signでrw-modeによるopenが出来る。
パイプの終了
読み込み側がcloseした時点で、書き込み側のwriteは失敗するようになる。
書き込み側がcloseして、なおかつ読み込み側が残り全てをreadしたらEOFend-of-fileにより終了する。
一度closeして、再び同じfifoファイルを開いても同じパイプには繋がらないため、読み込み側は続きのデータが欲しい間はcloseしてはいけない。
(この説明は正確ではない。再openでも同じパイプから続きのデータを読める状況は存在する。下記参照)
余談, パイプの終了, 複数の書き込み側, 複数の読み込み側
別ページ [ffmpeg: 任意の画像加工コマンドで動画を加工する] の余談と、複数の書き込み側と複数の読み込み側がある場合のfifo-pipeの挙動の説明をする。
exec 121> "${fifo_s2_to_s3}"など、リンク先の実装例ではブロッキングを考慮したために書き方が複雑化した。<>を使えばシンプルに表現出来たかも知れない。
さらに言えばfifoは複数の書き込み側と複数の読み込み側という組み合わせをサポートしている。
この仕様を使った場合も、コードがシンプルになったかも知れない。
複数の書き込み側
複数の書き込み側によって並列して書き込まれたデータは直列に並び変えられて読み込み側に届く。
この挙動は通常ファイルに並列に書き込んだ際の結果と異なる。
複数の読み込み側
複数の読み込み側がある場合にそれぞれの読み込み側に来るデータはどうなっているか?
想定できる次の二つの動作のうち、正解は後者である。
-
[誤]: 全ての読み込み側に同じデータが来る
-
[正]: 製造ライン作業のように手が空いた読み込み側が先頭から取っていく。
読み込み側も書き込み側と同じ様に直列に読み込んでいく仕様である。
複数の読み書きがある際のパイプの終了
挙動を予測しにくいのがパイプの終了に関するこの仕様。
一つ以上の書き込み側がopen中ならば書き込みは継続しているとパイプに見なされる。
一つ以上の読み込み側がopen中ならば読み込みは継続しているとパイプに見なされる。
つまり、書き込み側も読み込み側も同役割のfile descriptorが一つ以上存在してさえいれば、一旦closeしてから再びopenしても、同じパイプに対して書き込みや読み込みを再開することが出来る。
このパイプの終了を回避出来る仕様を使えばbashの並列実行でfifoをopenだけして1byteも読み込まない、読み込み意思表示キーパーを置いておけば、[ffmpeg: 任意の画像加工コマンドで動画を加工する]でFD操作なしで書けたかも知れない。
その場合はきっと、いささか乱暴なジョブコントロールが必要になっただろうが。