プロセス間通信のためのメッセージ送受信用ファイルハンドルのペアを生成する
pipe
は、fork
を使ってフォークした子プロセスと通信するためのパイプ(通信路)を生成します。
pipe
によって生成されるパイプとは、送信用ファイルハンドルと受信用ファイルハンドルのことです。
送信用ファイルハンドルを使ってメッセージを書き込むと、受信用ファイルハンドルでそのメッセージを受信できます。
そのファイルハンドルの一方を親プロセスが使い、もう一方を子プロセスが使うことで、片方向の通信路が実現されます。
pipe
はパイプ生成に失敗すると、偽を返します。
pipe( my $reader, my $writer ) or die "パイプの生成に失敗しました: $!";
$reader
と $writer
に、それぞれ受信用ファイルハンドルと送信用ファイルハンドルがセットされます。
pipe
によって生成されたパイプを通して送信されるメッセージは、IO バッファリングを使います。
もしメッセージ送受信にリアルタイム性を求めるなら、送信用ファイルハンドルに対して
$|
に 1
をセットして、オートフラッシュを有効にしてください。
もしオートフラッシュを有効にしないと、とりわけ小さいメッセージを何度も送信するようなケースでは、
ある一定のデータがたまってからまとめて相手に送られるという現象が発生し、
リアルタイム性が損なわれますので注意してください。
以下のサンプルコードは、子プロセスが親プロセスに向けてメッセージを 1 秒おきに 3 回送信します。
# パイプを生成 (受信用ファイルハンドルと送信用ファイルハンドルを生成)
pipe( my $reader, my $writer ) or die "パイプの生成に失敗しました: $!";
# 子プロセスを生成
my $pid = fork();
unless ( defined $pid ) {
die "子プロセスの生成に失敗しました: $!";
}
# 親プロセスの処理
if ($pid) {
# 送信用ファイルハンドルは不要なため閉じる
close $writer;
# 子プロセスからのメッセージを待ち受ける
while ( my $msg = <$reader> ) {
print $msg;
# 子プロセスの送信用ファイルハンドルを閉じると、このループは終了します。
}
# 受信用ファイルハンドルを閉じる
close $reader;
# 子プロセスの終了を待つ
waitpid( $pid, 0 );
}
# 子プロセスの処理
else {
# 受信用ファイルハンドルは不要なため閉じる
close $reader;
# 送信用ファイルハンドルのオートフラッシュを有効にする
my $default_fh = select $writer;
$| = 1;
select $default_fh; # $default_fh は通常は STDOUT
# 1 秒おきに 3 回メッセージを送信する
for ( my $i = 1 ; $i <= 3 ; $i++ ) {
print $writer "MESSAGE ${i}\n";
sleep 1;
}
# 送信用ファイルハンドルを閉じる
close $writer;
# 子プロセスを終了する
exit;
}
上記サンプルコードでは 2 点注目してください。
親プロセスの処理と子プロセスの処理それぞれで、使わないファイルハンドルを
close
で閉じています。
親と子で一つでも閉じられていないファイルハンドルが残っているとプロセスが終了しません。
漏れがないよう、使わないファイルハンドルは早いうちに閉じてください。
また、使い終わったファイルハンドルも必ず閉じてください。
とりわけ、子プロセスの送信用ファイルハンドルを閉じないと、
親プロセスの受信用フィルハンドルの while
ループが終わりませんので注意してください。
上記サンプルコードでは、メッセージ送受信のリアルタイム性を重視して、
送信用ファイルハンドルのオートフラッシュを有効にしています。
select
の使い方と $|
の使い方を参考にしてください。
もしオートフラッシュを有効にしないと、このサンプルコードでは親プロセスはリアルタイムに子プロセスからのメッセージを受け取れません。
子プロセスから送信されたメッセージがバッファリングされてしまうため、
親プロセスでは、子プロセスが 3 回メッセージを送信し終わってから、まとめて連続して 3 通のメッセージを受け取ることになります。