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(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{...} に関数名を代入することで、シグナル受信時の処理(シグナルハンドラ)を指定することができます。... の部分には 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 { # ロックフォルダの削除や、重要データの緊急退避などの後処理 }
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 ではまだサポートされていません。