はじめに
PHPでマルチプロセス処理を行う際、子プロセスがどのように終了したかを把握することは非常に重要です。特に、子プロセスがシグナルによって終了した場合、そのシグナル番号を知ることでデバッグやエラーハンドリングが格段に楽になります。
今回は、そんな場面で活躍するpcntl_wtermsig関数について、初心者の方にも分かりやすく詳しく解説していきます。
pcntl_wtermsig関数とは?
pcntl_wtermsig
は、子プロセスがシグナルによって終了した場合に、そのシグナル番号を取得するためのPHP関数です。
基本構文
pcntl_wtermsig(int $status): int|false
パラメータ:
$status
–pcntl_waitpid()
やpcntl_wait()
で取得したステータス値
戻り値:
- 子プロセスを終了させたシグナル番号(成功時)
false
(失敗時)
使用する前の重要な注意点
1. pcntl拡張モジュールが必要
この関数を使用するには、PHPがpcntl拡張モジュール付きでコンパイルされている必要があります。
// pcntl拡張が有効か確認
if (!function_exists('pcntl_fork')) {
die("pcntl拡張が利用できません\n");
}
2. Windowsでは使用不可
pcntl関数群はUnix/Linux系システム専用です。Windowsでは動作しません。
3. 使用順序が重要
pcntl_wtermsig
を使う前に、必ずpcntl_wifsignaled()
でシグナルによる終了かどうかを確認する必要があります。
実践的な使用例
基本的な使用方法
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die("フォークに失敗しました\n");
} elseif ($pid == 0) {
// 子プロセス
echo "子プロセス開始\n";
sleep(5);
exit(0);
} else {
// 親プロセス
$status = 0;
pcntl_waitpid($pid, $status);
// シグナルで終了したかチェック
if (pcntl_wifsignaled($status)) {
$signal = pcntl_wtermsig($status);
echo "子プロセスはシグナル {$signal} で終了しました\n";
// シグナル名を表示
switch ($signal) {
case SIGTERM:
echo "終了シグナル(SIGTERM)を受信\n";
break;
case SIGKILL:
echo "強制終了シグナル(SIGKILL)を受信\n";
break;
case SIGINT:
echo "中断シグナル(SIGINT)を受信\n";
break;
default:
echo "その他のシグナル\n";
}
} else {
echo "子プロセスは正常に終了しました\n";
}
}
より実践的な例:タイムアウト処理
<?php
function executeWithTimeout($callback, $timeout = 5) {
$pid = pcntl_fork();
if ($pid == -1) {
throw new Exception("フォークに失敗");
} elseif ($pid == 0) {
// 子プロセスでコールバック実行
$callback();
exit(0);
} else {
// 親プロセス
$start = time();
$status = 0;
// タイムアウトチェック付きで待機
while (time() - $start < $timeout) {
$result = pcntl_waitpid($pid, $status, WNOHANG);
if ($result == -1) {
throw new Exception("waitpidエラー");
} elseif ($result > 0) {
// 子プロセスが終了
if (pcntl_wifsignaled($status)) {
$signal = pcntl_wtermsig($status);
echo "シグナル {$signal} で終了\n";
}
return true;
}
usleep(100000); // 0.1秒待機
}
// タイムアウト:子プロセスを強制終了
echo "タイムアウト!子プロセスを終了します\n";
posix_kill($pid, SIGTERM);
sleep(1);
posix_kill($pid, SIGKILL);
pcntl_waitpid($pid, $status);
return false;
}
}
// 使用例
executeWithTimeout(function() {
echo "重い処理を実行中...\n";
sleep(10); // 10秒かかる処理
}, 3); // 3秒でタイムアウト
よくあるシグナルの種類
シグナル定数 | 番号 | 説明 |
---|---|---|
SIGTERM | 15 | 終了要求(正常終了) |
SIGKILL | 9 | 強制終了(キャッチ不可) |
SIGINT | 2 | 中断(Ctrl+C) |
SIGSEGV | 11 | セグメンテーションフォルト |
SIGALRM | 14 | アラームタイマー |
SIGCHLD | 17 | 子プロセス状態変化 |
デバッグのベストプラクティス
シグナル情報をログに記録する
function logChildExit($status) {
if (pcntl_wifsignaled($status)) {
$signal = pcntl_wtermsig($status);
$signalName = getSignalName($signal);
error_log("子プロセスがシグナルで終了: {$signalName} ({$signal})");
// コアダンプの有無も確認
if (pcntl_wcoredump($status)) {
error_log("コアダンプが生成されました");
}
} elseif (pcntl_wifexited($status)) {
$exitCode = pcntl_wexitstatus($status);
error_log("子プロセスが正常終了: 終了コード {$exitCode}");
}
}
function getSignalName($signal) {
$signals = [
SIGTERM => 'SIGTERM',
SIGKILL => 'SIGKILL',
SIGINT => 'SIGINT',
SIGSEGV => 'SIGSEGV',
SIGALRM => 'SIGALRM',
];
return $signals[$signal] ?? "UNKNOWN({$signal})";
}
まとめ
pcntl_wtermsig
関数は、マルチプロセスプログラミングにおいて子プロセスの異常終了を検知するための重要な関数です。
覚えておくべきポイント:
- 必ず
pcntl_wifsignaled()
で事前チェックする - シグナル番号から適切なエラーハンドリングを行う
- ログ出力でデバッグを容易にする
- Unix/Linux環境専用であることを忘れずに
この関数を適切に使用することで、より堅牢なマルチプロセスアプリケーションを構築できます。実際のプロジェクトで活用してみてください!