ファイヤープロジェクト
pipe
2003-07-20T15:13+09:00   matsu
popenとpcloseがsystem関数のパイプ版だと考えると,pipeはexecのpipe版と考えられるだろうか.
pipeはpopenやpcloseと異なって,shell経由でコマンドを起動するものではないのでオーバーヘッドは小さいが,ワイルドカードなどの展開はできない.というか,コマンドを起動しない(引数にもない).pipe関数は入力用と出力用のファイルディスクリプタ(ファイルストリームではない)を返して来る.まずはこれをforkした子との通信に使用するサンプル.
pipe関数を実行すると,引数int pipes[2]にパイプのファイルディスクリプタを突っ込んでくれる.で,forkではファイルディスクリプタも子に継承されるので,子でもパイプのファイルディスクリプタをそのまま使用できる.で,そのファイルディスクリプタは親と同じものなので,この親子はパイプを介して通信できる.以下実行結果.
write : hogefugafoo     11 bytes
read : hogefugafoo      11 bytes
popen,pcloseの組合せではpopenの際に必ず読み込み用か書き出し用かの指定をして,パイプのファイルストリームを取得していた.pipeでは読み書きの指定はせず,引数で渡した領域に読み込み用と書き込みようのファイルディスクリプタを渡してくれる.
先のサンプルはforkして子が親からパイプのファイルディスクリプタを引き継いで親子で通信した.子がさらにexecしてパイプのファイルディスクリプタを使用するには,execの起動引数で渡す必要がある.以下サンプル.まずはforkして子がexecするサンプル.
親はパイプを介して子からのメッセージを受け取って表示する.そして子プロセスにパイプを介してメッセージを渡す.子プロセスはexecで以下を呼び出す.
子は親とは逆にまずメッセージを親に送り,次に親からメッセージを受け取る.後者のサンプルでDelayとコメントされているsleepは,この二つのサンプルの問題点を象徴している(というかちょっとハマってやっつけ的な対策がこのsleepなんだけど).このsleepがなければ子が親に向けたメッセージを子自身が読み取ってしまうことがある.プロセスの切替えタイミングの問題である.で,少しsleepさせている.が,これは問題解決を保証するものではない.本来ならシグナルなどを使用して同期をとるべきだろう.以下実行結果.
2456    checking child boot
2457    child booted
2457    sended boot_msg
2456    got message "I'm booted"
2456    checked child boot
2456    write msg to child
2457    child read
2457    got message "This is parent msg"
子プロセスでexecするサンプルを作成する際に,同期の問題とは別にもう一つはまった点がある.whileでreadすると,readが終らない問題である.何故か.パイプのファイルディスクリプタをcloseしていないからであった.
上のサンプルでNOT_CLOSE_PARENTをdefineしてコンパイルすると親が書き込み用のパイプをクローズしない.NOT_CLOSE_CHILDをdefineしてコンパイルすると子が書き込みようのパイプをクローズしない.だから,これらのどちらか一方でもdefineすると子プロセスのreadが終了しなくなってしまう.pipeを使用するときは,適切なタイミングでcloseすることにも注意していかないといけない.
先のforkしてexecするサンプルでは,execするプログラムが引数にファイルディスクリプタを取っていた.が,これはちょっと変態的な雰囲気なので,何とかしたい.それにはファイルディスクリプタ0をパイプにする.
dupを実行すると,ファイルディスクリプタの複製を返す.その値はその時点で使用可能なファイルディスクリプタを返す.これによって標準入力をパイプに結びつけてexecしている.これを実行すると
hogefugafoo
と出力される.子プロセスがexecしたcatの標準入力がパイプと結びついているので,親プロセスの書き込みがcatの出力として出てきている.
matsu(C)
Since 2002
Mail to matsu