仕様
再生成されたプロセスがシグナルを処理していないときに、再生成する前に処理が正しく機能しているとき、PHP に問題があります。コードを非常に基本的なものに絞り込みました。
declare(ticks=1);
register_shutdown_function(function() {
if ($noRethrow = ob_get_contents()) {
ob_end_clean();
exit;
}
system('/usr/bin/nohup /usr/bin/php '.__FILE__. ' 1>/dev/null 2>/dev/null &');
});
function handler($signal)
{
switch ($signal) {
case SIGTERM:
file_put_contents(__FILE__.'.log', sprintf('Terminated [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
ob_start();
echo($signal);
exit;
case SIGCONT:
file_put_contents(__FILE__.'.log', sprintf('Restarted [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
exit;
}
}
pcntl_signal(SIGTERM, 'handler');
pcntl_signal(SIGCONT, 'handler');
while(1) {
if (time() % 5 == 0) {
file_put_contents(__FILE__.'.log', sprintf('Idle [ppid=%s] [pid=%s]'.PHP_EOL, posix_getppid(), posix_getpid()), FILE_APPEND);
}
sleep(1);
}
ご覧のとおり、次のことを行います。
- プロセスを再起動するシャットダウン関数を登録します(親プロセスが終了したとき
nohup
に無視するため)SIGHUP
pcntl_signal()
forSIGTERM
およびを介してハンドラーを登録しSIGCONT
ます。最初はプロセスが終了したというメッセージを記録するだけですが、2番目はプロセスの再生成につながります。これはob_*
関数で実現されるため、フラグを渡すには、シャットダウン関数で何を行う必要がありますか - 終了または再生成のいずれかです。- スクリプトが「生きている」という情報をログ ファイルに記録します。
何が起こっている
だから、私はスクリプトを開始しています:
/usr/bin/nohup /usr/bin/php script.php 1>/dev/null 2>/dev/null &
次に、ログ ファイルに次のようなエントリがあります。
Idle [ppid=7171] [pid=8849]
Idle [ppid=7171] [pid=8849]
たとえば、私は次のようにしますkill 8849
。
Terminated [ppid=7171] [pid=8849]
したがって、正常に処理されSIGTERM
ます (そしてスクリプトは実際に終了します)。ここで、代わりに を実行するkill -18 8849
と、(18 は の数値SIGCONT
)が表示されます。
Idle [ppid=7171] [pid=8849]
Restarted [ppid=7171] [pid=8849]
Idle [ppid=1] [pid=8875]
Idle [ppid=1] [pid=8875]
したがって、最初はSIGCONT
正しく処理され、次の「アイドル」メッセージから判断すると、新しく生成されたスクリプトのインスタンスはうまく機能しています。
更新#1:(ppid=1
したがって、init
グローバルプロセス)および孤立プロセスのシグナル処理について考えていましたが、そうではありません。これは、orphan ( ) プロセスが理由ではないことを示しています。ppid=1
アプリを制御してワーカーを起動すると、system()
ワーカーが自分自身を再生成するのと同じように、コマンドで呼び出します。ただし、制御アプリがワーカーを呼び出した後、ppid=1
シグナルを正しく受信して応答しますが、ワーカーが自分自身を再生成した場合、新しいコピーはそれらに応答しませんSIGKILL
。そのため、ワーカーが自分自身を再生成する場合にのみ問題が発生します。
更新 #2 : で何が起こっているのかを分析しようとしましたstrace
。さて、ここに2つのブロックがあります。
- ワーカーがまだ再生成されていない場合-strace の出力。行
4
と を5
SIGCONT
見てください。これはkill -18
、プロセスに送信するときです。そして、すべてのチェーンをトリガーします: ファイルへの書き込み、system()
現在のプロセスの呼び出しと終了。 ワーカーがすでにそれ自体で再生成されている場合 - strace の出力。ここで、行
8
を9
見てください- それらは を受け取った後に現れましたSIGCONT
。1 つ目: プロセスがまだ何らかの形でシグナルを受信しているように見え、2 つ目はシグナルを無視することです。アクションは実行されませんでしたが、送信されたシステムによってプロセスが通知されましたSIGCONT
。なぜプロセスがそれを無視するのか - 問題です (ユーザー ハンドラのインストールがSIGCONT
失敗した場合、プロセスは終了せずに実行を終了する必要があるため)。に関してはSIGKILL
、すでにリスポーンされたワーカーの出力は次のようになります。nanosleep({1, 0}, <unfinished ...> +++ killed by SIGKILL +++
これは、そのシグナルが受信され、本来すべきことを行ったことを示しています。
問題
SIGTERM
プロセスがリスポーンされるため、 にも にも反応しませんSIGCONT
。ただし、それを終了することはまだ可能ですSIGKILL
(したがって、kill -9 PID
実際にプロセスを終了します)。たとえば、両方の上のプロセスの場合、何もkill 8875
しkill -18 8875
ません (プロセスはシグナルを無視し、メッセージを記録し続けます)。
ただし、シグナルの登録が完全に失敗しているとは言いません-少なくとも再定義するためですSIGTERM
(通常は終了につながりますが、この場合は無視されます)。またppid = 1
、何か間違ったことを指しているのではないかと思いますが、今ははっきりとは言えません。
また、他の種類のシグナルを試しました(実際、シグナルコードが何であるかは関係ありませんでした。結果は常に同じでした)
質問
そのような行動の理由は何ですか?プロセスを再生成する方法は正しいですか? そうでない場合、新しく生成されたプロセスがユーザー定義のシグナルハンドラーを正しく使用できるようにする他のオプションは何ですか?