mkfifo() で生成した名前付きパイプをopen()するときにブロックすることがある話です..
マニュアルの片隅に書いてあって、あまり知られてないことかと…
ちょっとこれでハマったので備忘録として残しておきます.
Table of Contents
mkfifo
mkfifo()は以下のような形式で、pathnameで指定された名前付きパイプ(FIFOスペシャルファイル)を生成します.
名前付きパイプは通常のファイル同様、どのようなプロセスからもオープン可能、読み込み、書き込みが可能です.
名前付きパイプは以下の様に生成できます.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main()
{
int st = mkfifo("/tmp/pipe", 0666);
if (st < 0)
{
perror ("mkfifo");
return -1;
}
return 0;
}
生成したパイプは通常のファイルのように見えます.
やりたいこと
スレッド間 (あるいはプロセス間) 名前付きパイプで通信したいと考えました.
スレッド A からスレッドBにメッセージを名前付きパイプ経由で伝えたい
図で書くとこんな感じ
- スレッドAは名前付きパイプに対して書き込みのみ.
- スレッドBは名前付きパイプに対して読み込みのみ.
という制御となります.
パイプのオープン
スレッドBで名前付きパイプをオープンする際、読み込みだけなので Read Only 指定でオープンしました.
int fd = open ("/tmp/pipe", O_RDONLY);
if (fd < 0)
{
perror ("open");
return -1;
}
そうしたら、
open ()でブロックするではないですか!!
メッセージがやってくるまで read() でブロックするのは分かりますが、open()でブロックするとは…. なぜだ?
相手がオープンするまでブロックする
man open(2) を読んでみました.
Opening the read or write end of a FIFO blocks until the other end is also opened (by another process or thread). See fifo(7) for further details.
名前付きパイプ(FIFO)をRead/Wirteでopenする場合, 相手がopenするまでブロックされる.
と書かれてました. なるほど.
詳しくは fifo (7) を見ろ、とあります.
カーネルは、少なくとも一つのプロセスによってオープンされている FIFO 特殊ファイルに関して、 1 つの FIFO 特殊ファイルにつき 1 つのパイプオブジェクトを管理する。データを渡す前に、 FIFO の両端 (書き込み側と読み出し側) がオープンされていなければならない。通常、 FIFO をオープンしようとした場合、その反対側がオープンされるまで停止 (block) される。
とありました. まず fifo の man を見るべきでした.
オープンでブロックするのを回避するには
man fifo (7) を見るとオープン時にブロックするのを回避する方法が書いてました.
読み書きモードでオープンする
読み書きモード O_RDWR でオープンするとブロックを回避できます.
ただし以下の点を注意. fifo(7) より
Linux では、 FIFO を読み書き両用でオープンした場合、 停止、非停止のどちらのモードでも成功する。 POSIX では、この場合の動作は定義されていない。この動作は、読み込み側がいない時に書き込み用に FIFO をオープンするために使用できる。自分自身と通信するために FIFO の両端を使用するプロセスでは、デッドロックを避けるために細心の注意を払う必要がある。
ノン・ブロックモードでオープンする
文字通り、ノンブロックモード O_NONBLOCK でオープンすると読み込み専用 O_RDONLY, 書き込み専用 O_WRONLY を選択してもブロックすることはありません.
以下の点を注意ください. fifo(7) からの引用.
プロセスは FIFO を非停止 (nonblocking) モードでオープンすることもできる。この場合、読み込み専用でオープンしようとした場合には、書き込み側を誰もオープンしていなくても成功する。書き込み専用でオープンしようとした場合には、反対側がすでにオープンされていない限り、 ENXIO (そのようなデバイスまたはアドレスは存在しない) というエラーで失敗する。
当然のことながら, O_NONBLOCK でオープンした場合, データが来るのを read()が待たないので、その点を考慮に入れたプログラミングが必要です.
今回は man コマンド中心に調べましたが, 書籍であればこれが良いかなぁ、と思います.
それでは.
“【備忘録】名前付きパイプをopen()するとブロックする件” への1件の返信
コメントは受け付けていません。