前置き

元々は​[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​は​複数の書き込み側​と​複数の読み込み側​という組み合わせをサポートしている。
この仕様を使った場合も、コードがシンプルになったかも知れない。

複数の書き込み側

複数の書き込み側​によって並列して書き込まれたデータは直列に並び変えられて読み込み側に届く。
この挙動は通常ファイルに並列に書き込んだ際の結果と異なる。

[複数の書き込み側の動作例]

writer1​が​"abcd"​、​writer2​が​"123"​を同時並列に書き込んだとする。

通常ファイル​が書き込み先の場合、結果は​"abcd"​や​"123"​あるいはややこしく​"123d", "1b3d"​などの​切り詰め​られて​上書き​された結果の組み合わせである。

fifoファイル​が書き込み先の場合、結果は​"abcd123"​や​"123abcd"​あるいはややこしく​"a12bc3d"​など​直列に並べ​られた組み合わせである。

(1byte​単位でデータが混ざる実行環境は普通はない。書き込み単位、バッファーサイズ、改行などの区切り文字の単位で混ざる)

複数の読み込み側

複数の読み込み側​がある場合にそれぞれの読み込み側に来るデータはどうなっているか?
想定できる次の二つの動作のうち、正解は後者である。

  • [誤]: 全ての読み込み側に同じデータが来る

  • [正]: 製造ライン作業のように手が空いた読み込み側が先頭から取っていく。

読み込み側も書き込み側と同じ様に直列に読み込んでいく仕様である。

複数の読み書きがある際のパイプの終了

挙動を予測しにくいのがパイプの終了に関するこの仕様。

一つ以上の書き込み側が​open​中ならば書き込みは継続しているとパイプに見なされる。
一つ以上の読み込み側が​open​中ならば読み込みは継続しているとパイプに見なされる。

つまり、書き込み側も読み込み側も同役割の​file descriptor​が一つ以上存在してさえいれば、一旦​close​してから再び​open​しても、同じパイプに対して書き込みや読み込みを再開することが出来る。

このパイプの終了を回避出来る仕様を使えば​bash​の並列実行で​fifo​を​open​だけして​1byte​も読み込まない、読み込み意思表示キーパーを置いておけば、​[ffmpeg: 任意の画像加工コマンドで動画を加工する]​で​FD操作​なしで書けたかも知れない。

その場合はきっと、いささか乱暴なジョブコントロールが必要になっただろうが。