「名前付きパイプ」(FIFO/Named Pipe)を使ってプロセス間通信を試してみる
はじめに
Bash のストリーム/パイプ処理を視覚的に理解する のエントリで触れましたが、「名前付きパイプ」というものがあって今のプロジェクトで使う可能性が出てきたので取り上げてみました。
本稼働するシステムで使うことは初めてになりそうなので、楽しみです。
検証環境
$ uname -moi
x86_64 MacBookPro11,4 Darwin
$ bash -version
GNU bash, バージョン 5.0.2(1)-release (x86_64-apple-darwin18.2.0)
通常のパイプ
「通常のパイプ」は「無名」です。
使用しているプロセスが動作中の間だけ存在します
プロセスが終了するとともに消えてしまいます。
パイプを挟んで前後にプロセスが 1 つずつ存在します。
一方は「パイプ」へ出力、他方は「パイプ」からの入力を待ちます。
(例)
# `seq` で 1から10 の数値を出力し、 `tail` で入力を受け付ける。
$ seq 1 10 | tail -f
"名前付きパイプ" とは?
- 「名前付きパイプ」 は「ファイル名」を持ちます。
- プロセスが消滅しても存在し続けます。(そのため、使わなくなったら削除する必要があります。)
- プロセス間通信 (IPC) を行うためにプロセスがオープンして使用します。
試してみる
簡単な例を上げます。
名前付きパイプの作成
mkfifo
というコマンドを実行すると、 「名前付きパイプ」 ファイルが作成できます。
ここでは Named Pipe の頭文字をとって np
としています。
$ mkfifo np
ls -l
コマンドでファイルができたか確認すると、
ファイルタイプ(一番左の文字)を見ると、 p
となっていますが、このファイルが名前付きパイプであることを示しています。
$ ls -l np
prw-rw---- 1 genzouw wheel 0 3 18 08:00 np
名前付きパイプへの Input/Output
ここでターミナルを2つ開きます。
一方のターミナルで以下のコマンドを実行します。
$ echo hello > np
実行してもコマンドが終了しません。
この状態でもう一方のターミナルで以下のコマンドを実行します。
$ cat np
hello
cat
を実行したターミナルのプロセスは即座に終了しましたが、はじめに echo
したプロセスも同時に終了します。
「名前付きパイプ」 は、パイプに対して出力するプロセス、入力するプロセスをペアリングし、両者の出力・入力を紐付けるための仕組みを提供します。
「名前付きパイプ」に対して複数プロセスから書き込みしても大丈夫なの?欠損とか起きない?
「ファイル」という姿をしているため、複数プロセスから書き込みした場合に欠損など起きるのではないかという点を懸念したので、試してみます。
ターミナルを 4 枚開き、以下のコマンドを同時に実行することで、4 プロセスから名前付きパイプに書き込みを行います。
# ターミナルその1
$ for ((i=0; i<10000; i++)); do
echo "ps1" >> np
done
# ターミナルその2
$ for ((i=0; i<10000; i++)); do
echo "ps2" >> np
done
# ターミナルその3
$ for ((i=0; i<10000; i++)); do
echo "ps3" >> np
done
# ターミナルその4
$ for ((i=0; i<10000; i++)); do
echo "ps4" >> np
done
更にターミナルを 1 枚開き、以下のコマンドを実行してデータを読み取ります。
( ファイルを読み込み、コンソール出力させながら out.txt
にも書き出しておきます。 )
$ tail -f np | tee -a out.txt
数秒後、合計 5 つのプロセスは正常に終了しました。out.txt
の内容を確認し、ファイルの内容に欠損が無いか確認してみます。
$ cat out.txt | wc -l
40000
$ grep ps1 out.txt | wc -l
10000
$ grep ps2 out.txt | wc -l
10000
$ grep ps3 out.txt | wc -l
10000
$ grep ps4 out.txt | wc -l
10000
重複、欠損はなさそうですね。
「名前付きパイプ」に対して複数プロセスから読み取りしても大丈夫なの?
逆も試してみます。
今度は複数プロセスから読み取りしてみます。
# ターミナルその1
$ while true; do
cat np >> ps1.txt
done
# ターミナルその2
$ while true; do
cat np >> ps2.txt
done
# ターミナルその3
$ while true; do
cat np >> ps3.txt
done
# ターミナルその4
$ while true; do
cat np >> ps4.txt
done
更にターミナルを 1 枚開き、以下のコマンドを実行してデータを名前付きパイプに書き込みます。
$ for ((i=0; i<40000; i++)); do
echo $i > np
done
echo: write error: broken pipe
echo: write error: broken pipe
echo: write error: broken pipe
echo: write error: broken pipe
echo: write error: broken pipe
echo: write error: broken pipe
...
なんだかめちゃくちゃエラーが出てます。
結果を確認してみます。
$ wc -l ps*.txt
6570 ps1.txt
7889 ps2.txt
6713 ps3.txt
7967 ps4.txt
29139 total
結果が全くあってないですね。なぜか出力漏れが発生しています。
名前付きパイプへの出力が漏れたのか、名前付きパイプからの入力が漏れたのか。
ひとこと
複数書き込みは使えそうでした。
複数読み込みは原因の調査が必要。
ディスカッション
コメント一覧
まだ、コメントがありません