プロセスとシグナル
■ 子プロセスを起動する(fork, exec)
◆ 子プロセスの生成
system() や `...` によるコマンド起動がサポートされているので、あまり使用されることはありませんが、低レベルのコマンド起動手順がサポートされています。
fork() は、親プロセス から 子プロセス を生成します。fork() を実行すると親プロセスと子プロセスに分岐し、双方が独立して動き始めます。子プロセスの場合は 0、親プロセスの場合は子プロセスの プロセスID が戻り値として返されます。
exec() は、指定したコマンドに処理を渡します。プロセスが新しいコマンドに変身するようなものです。fork() と exec() を組み合わせることで、コマンドを子プロセスとして実行することが可能になります。
wait() は、UNIX 環境で終了した子プロセスを成仏させるのに必要です。この処理を怠ると、子プロセスは ゾンビプロセス と呼ばれる状態でさまよいつづけることになります。特定の子プロセスを成仏させるには waitpid($pid) を用います。
$pid = fork();
if ($pid == -1) {
exit(1); # エラー処理
} elsif ($pid == 0) {
sleep(3); # 子プロセスの処理
exec("netstat -a");
} else {
for ($i = 0; $i < 6; $i++) { # 親プロセスの処理
print "===PARENT($i)\n";
sleep(1);
}
wait();
}
実行結果は次のようになります。
===PARENT(0) ===PARENT(1) ===PARENT(2) Active Connections Proto Local Address Foreign Address State TCP pc2:135 PC2:0 LISTENING TCP pc2:6543 PC2:0 LISTENING ===PARENT(3) ===PARENT(4) ===PARENT(5)
■ プロセスにシグナルを送る(kill)
◆ プロセスにシグナルを送る
kill(sig, pid) は、pid で指定したプロセスに sig で指定したシグナルを送信します。指定可能なシグナルは「主なシグナル一覧」を参照してください。下記の例では、親プロセスから子プロセスを起動し、3秒後に親プロセスから子プロセスに SIGINT(2) シグナルを送信しています。SIGINT(2) シグナルを受け取った子プロセスは、処理の途中でプロセスを中断して終了します。
$| = 1; # バッファリングをオフにしておく
$pid = fork(); # 子プロセスに分岐する
if ($pid == 0) {
# 子プロセス
for ($i = 0; $i < 10; $i++) {
print "===CHILD($i)\n";
sleep(1);
}
} else {
# 親プロセス
sleep(3);
kill(2, $pid);
wait();
}
実行結果は次のようになります。
===CHILD(0) ===CHILD(1) ===CHILD(2)
■ シグナル受信時の処理を指定する($SIG{...})
◆ シグナルハンドラを指定する
$SIG{...} に関数名を代入することで、シグナル受信時の処理(シグナルハンドラ)を指定することができます。... の部分には INT、TERM、ALRM など、シグナル名から SIG を取り除いた名前を指定します。シグナル名は「主なシグナル一覧」を参照してください。
$| = 1; # バッファリングしないモード
$SIG{'INT'} = "sig"; # シグナルハンドラの設定
for ($i = 0; $i < 40; $i++) {
print "."; # 0.1秒毎に . を書き出す
select(undef, undef, undef, 0.1); # 0.1秒間ウェイトする
}
print "\n";
# SIGINT シグナル受信時に呼ばれる
sub sig {
local($type) = @_;
$SIG{'INT'} = "sig"; # シグナルハンドらの再設定
print "<SIG$type>";
}
これをコマンドプロンプトから実行し、ドット(.)が書き出されている間にキーボードから Ctrl-C(Ctrl キーを押しながら C)を押すとプロセスに SIGINT シグナルが送信されます。プロセスはこれを受信し、$SIG{'INT'} の値に従って sig サブルーチンを呼び出します。実行例は次のようになります。
....<SIGINT>.............<SIGINT>..........<SIGINT>.............
◆ プロセス中断時の処理を指定する
CGI がタイムアウトしてしまったり、プロセスが強制終了させられるときには、プロセスに SIGINT、SIGTERM、SIGHUP、SIGQUIT などのシグナルが送信されます。これらのシグナルをキャプチャして、シグナルハンドラを呼び出し、強制終了時の後処理を実行することができます。
$SIG{'INT'} = $SIG{'TERM'} = $SIG{'HUP'} = $SIG{'QUIT'} = "sigexit";
sub sigexit {
# ロックフォルダの削除や、重要データの緊急退避などの後処理
}
■ アラームシグナルを使う
◆ n秒後にアラームシグナルを送信する
alarm() を呼び出すと、指定した秒数後に自分自身に対して SIGALRM シグナルを送信します。プロセスはこれを $SIG{'ALRM'} でキャプチャしてシグナルハンドラを呼び出すことができます。
$| = 1; # バッファリングをオフにする
$SIG{'ALRM'} = "sig"; # SIGALRM 受信時のハンドラを指定
alarm(1); # 1秒後に SIGALRM を発生させる
for ($i = 0; $i < 100; $i++) {
print ".";
select(undef, undef, undef, 0.1); # 0.1秒間ウェイトする
}
print "\n";
sub sig {
local($type) = @_;
alarm(1); # アラームを再設定する
print "<SIG$type>";
}
これを実行すると次のように、1秒毎に <SIGALRM> が割り込み出力されます。
..........<SIGALRM>..........<SIGALRM>..........<SIGALRM>..........<SIGA LRM>..........<SIGALRM>..........<SIGALRM>..........<SIGALRM>..........< SIGALRM>..........<SIGALRM>..........<SIGALRM>
alarm() は、Windows XP ではまだサポートされていません。