ホームページ >バックエンド開発 >PHPチュートリアル >PHP プロセスの信号処理を 1 つの記事で徹底的に理解する

PHP プロセスの信号処理を 1 つの記事で徹底的に理解する

藏色散人
藏色散人転載
2023-04-01 07:30:032078ブラウズ

この記事は、PHP に関する関連知識を提供するもので、主に PHP のプロセス信号処理について詳しく紹介しています。興味のある友達は一緒に見てください。皆様のお役に立てれば幸いです。

PHP プロセスの信号処理を 1 つの記事で徹底的に理解する

#背景

上司が私のために手配してくれました。数週間前、私はタスクを開始し、シグナルをリッスンするパッケージを作成しました。当社のプロジェクトはコンテナ内で実行されるため、オンラインになるたびにイメージを再パッケージして開始する必要があります。再パッケージ化する前に、Dokcer は最初にコンテナーにシグナルを送信し、次にタイムアウト期間 (デフォルトは 10 秒) 待ってから、SIGKILL シグナルを送信してコンテナーを終了します。

これで、常駐コンテナ内のプロセスの場合、この常駐プロセスのタスクは、キュー内のメッセージを継続的に消費することです。今すぐオンラインにしたいのでコンテナをシャットダウンする必要があるとします。Docker はコンテナ内で実行されている常駐プロセスにシグナルを送信し、10 秒後にシャットダウンすることを伝えます。9 秒が経過し、常駐プロセスが終了したとします。プロセスがキューから取り出されたところです。メッセージの場合、1 秒以内に後続のロジックが実行される前にプロセスが強制終了されました。このとき、メッセージは失われ、ダーティ データが生成される可能性があります。

上記はこのタスクの背景であり、信号を監視して続行方法を決定する必要があります。上記の状況では、常駐プロセスが Docker によって送信されたシャットダウン シグナルを受信すると、プロセスはブロックされ、コンテナーが強制終了されるまでスリープする可能性があります。 OK、背景を明確にした後、PHP でシグナルを紹介しましょう (このパッケージの書き方については後で記事をまとめ、必要な友人が使用できるように https://packagist.org/ にパッケージを公開します) [推奨される学習 :

PHP ビデオ チュートリアル #]

1. Linux オペレーティング システムの信号とは何ですか

1. シグナルの概要

#シグナルは、イベント発生時のプロセスへの通知メカニズムであり、## とも呼ばれます。 #ソフトウェアは

を中断します。プロセスは別のプロセスにシグナルを送信することができ、例えば子プロセスが終了すると親プロセスにSIGCHLD(シグナル番号17)を送信して通知するため、シグナルがインターバルとして使用されることもあります。プロセス通信メカニズム。 Linux システムでは、通常、プロセスを終了するために kill -9 XXPID を使用します。実際、このコマンドの本質は、プロセスに SIGKILL (シグナル番号 9) を送信することです。フォアグラウンドのプロセスの場合、通常、実行を終了するには Ctrl c ショートカット キーを使用します。このショートカット キーの本質は、現在のプロセスに SIGINT (シグナル番号 2) を送信することであり、このシグナルを受信したときのプロセスのデフォルトの動作は実行を終了することです

2 、一般的に使用されるシグナルkill -l コマンドを使用すると、次のシグナルを表示できます

以下に、一般的に使用される重要な信号をいくつか紹介します。

#SIGQUIT2 プロセスの終了 (プロセスの割り込み) プログラムの終了 (割り込み、シグナル、ユーザーが INTR 文字 (通常は Ctrl-C、) を入力すると発行されますSIGQUIT3CORE ファイルを作成してプロセスを終了し、CORE ファイルを生成しますプロセスと CORE ファイルを生成します SIGQUIT は SIGINT に似ています、ただし、その前に QUIT 文字 (通常は Ctrl-、Control が付きます。プロセスは、SIGQUIT を受信して​​終了するときにコア ファイルを生成します。この意味で、これはプログラム エラー信号 ## に似ています。 #SIGFPESIGKILL##SIGSEGV11#SIGALRM14プロセスを終了します (タイマー期限切れ) SIGALRM クロック タイミング信号 (実際の時刻またはクロック時刻を計算します。アラーム機能はこの信号を使用します) SIGTERM15 プロセスを終了する (ソフトウェア終了シグナル)SIGTERM プログラム終了 (終了、シグナル。SIGKILL とは異なり、このシグナルはブロックされて処理されます。これは通常、プログラムが正常に終了するように要求するために使用されます。シェル コマンド kill は、デフォルトでこのシグナルを生成します。172629
信号名 信号値 信号タイプ 信号の説明
SIGHUP 1 プロセスを終了します (端末回線がハングアップします) このシグナルは、ユーザー端末の接続 (正常または異常) が終了したときに発行されます。 )、通常、端末の制御プロセスが終了すると、同じセッション内の各ジョブが通知されますが、この時点では、それらは制御端末とは関連付けられていません
8 CORE ファイルの作成 (浮動小数点例外) SIGFPE は、浮動小数点演算だけでなく致命的な算術演算エラーが発生した場合に発行されます。エラーだけでなく、オーバーフローや 0 による除算などの他のすべての算術エラーも含まれます
9 プロセスを終了する (プロセスを強制終了する) SIGKILL は、プログラムの実行を直ちに終了するために使用されます。このシグナルはブロックできず、処理および無視できません。
SIGSEGV は、それ自体に割り当てられていないメモリにアクセスしようとしています、または書き込み権限のないメモリ アドレスにデータを書き込もうとしています
#SIGCHLD
シグナルを無視します (子プロセスが停止または終了したときに親プロセスに通知します) SIGCHLD 子プロセスが終了すると、親プロセスはこのシグナルを受け取ります SIGVTALRM
プロセスを終了します (仮想タイマーの期限切れ) SIGVTALRM 仮想クロック信号。SIGALRM と似ていますが、プロセスが占有する CPU 時間を計算します SIGIO
シグナルを無視します (記述子 I/O で実行できます) SIGIO ファイル記述子を開始する準備ができています入出力操作

2. PHP の信号処理関連関数

PHP の pcntl 拡張機能と posix この拡張機能は、信号を操作するためのいくつかのメソッドを提供します (これらの機能を使用したい場合は、最初にこれらの拡張機能をインストールする必要があります)

以下は、このタスクで使用したいくつかのメソッドの詳細な紹介です。

declare

declare 構造体は、コードの実行命令を設定するために使用されます。宣言の構文は、他のフロー制御構造と似ています。

declare (directive)
    statement

ディレクティブ部分を使用すると、宣言コード セグメントの動作を設定できます。現在、認識される命令は ティックとエンコーディング の 2 つだけです。コード宣言セクションのステートメント部分が実行されます。実行方法と発生する副作用は、ディレクティブ

Ticks

Tick(クロック サイクル) は、インタプリタが宣言コード セグメント内の N 個の時間指定可能な 低レベル ステートメント を実行するたびに発生するイベントです。N の値は、宣言のディレクティブ部分の Nicks=N で指定します。すべてのステートメントに時間を設定できるわけではありません。通常、条件式パラメータ式は時刻指定できません。各ティックに表示されるイベントは register_tick_function() で指定されます。各ティックに複数のイベントが表示される可能性があることに注意してください。 詳細については、公式ドキュメントを参照してください: https://www.php.net/manual/zh/control-structurals.declare.php

<?php
declare(ticks=1);//每执行一条时,触发register_tick_function()注册的函数
$a=1;//在注册之前,不算
function test(){//定义一个函数
    echo "执行\n";
}
register_tick_function(&#39;test&#39;);//该条注册函数会被当成低级语句被执行
for($i=0;$i<=2;$i++){//for算一条低级语句
    $i=$i;//赋值算一条
}
输出:六个“执行”

pcntl_signal

pcntl_signal、信号プロセッサをインストールします

pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] ) : bool

関数 pcntl_signal() は、signo で指定された信号の新しい信号プロセッサをインストールします

declare(ticks = 1);
pcntl_signal(SIGINT,function(){
    echo "你按了Ctrl+C".PHP_EOL;
});
while(1){
    sleep(1);//死循环运行低级语句
}
输出:当按Ctrl+C之后,会输出“你按了Ctrl+C”

posix_kill

posix_kill、プロセスにシグナルを送信します

posix_kill ( int $pid , int $sig ) : bool

最初のパラメータはプロセス ID、2 番目のパラメータはプロセス ID です。パラメータ パラメータは送信するシグナルです。

a.php
<?php
declare(ticks = 1);
echo getmypid();//获取当前进程id
pcntl_signal(SIGINT,function(){
    echo "你给我发了SIGINT信号";
});
while(1){
    sleep(1);
}

b.php
<?php
posix_kill(执行1.php时输出的进程id, SIGINT);

##pcntl_signal_dispatch

pcntl_signal_dispatch。シグナルを待機しているプロセッサを呼び出します

pcntl_signal_dispatch ( void ) : bool

関数 pcntl_signal_dispatch() は、pcntl_signal() によってインストールされたプロセッサを通じて待機中の各シグナルを呼び出します。

<?php
echo "安装信号处理器...\n";
pcntl_signal(SIGHUP,  function($signo) {
     echo "信号处理器被调用\n";
});
echo "为自己生成SIGHUP信号...\n";
posix_kill(posix_getpid(), SIGHUP);
echo "分发...\n";
pcntl_signal_dispatch();
echo "完成\n";
?>

输出:
安装信号处理器...
为自己生成SIGHUP信号...
分发...
信号处理器被调用
完成

##pcntl_async_signals() 非同期信号処理は、ティックなしの非同期信号処理を有効にするために使用されます (これにより、多くの追加オーバーヘッドが発生します)。 (PHP>=7.1)

<?php
pcntl_async_signals(true); // turn on async signals

pcntl_signal(SIGHUP,  function($sig) {
    echo "SIGHUP\n";
});

posix_kill(posix_getpid(), SIGHUP);

输出:
SIGHUP

3. PHP でセマフォを処理する方法私たちは何を知っているかを知っています。信号を監視するには、declare(ticks=1) と pcntl_signal を組み合わせます。つまり、すべての低レベルの PHP ステートメントで、現在のプロセスに未処理の信号があるかどうかがチェックされます。これは、実際には非常にパフォーマンスを消費します。

pcntl_signal の実装原則は、シグナルをトリガーした後、まずシグナルをキューに追加することです。次にPHPのticksコールバック関数にシグナルがあるかどうかを継続的にチェックし、シグナルがある場合はPHPで指定されたコールバック関数を実行し、ない場合は関数から抜け出します。

PHP_MINIT_FUNCTION(pcntl)
{
 php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);
 php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);
 php_add_tick_function(pcntl_signal_dispatch TSRMLS_CC);

 return SUCCESS;
}

PHP5.3 以降には、pcntl_signal_dispatch 関数があります。現時点では、宣言は必要ありません。ループに関数を追加してシグナルを呼び出して渡すだけです:

<?php
echo getmypid();//获取当前进程id
pcntl_signal(SIGUSR1,function(){
    echo "触发信号用户自定义信号1";
});
while(1){
    pcntl_signal_dispatch();
    sleep(1);//死循环运行低级语句
}

PHP の Nicks=1 は、この関数が次の間隔でコールバックされることを意味することは誰もが知っています。 PHP コードの 1 行が実行される時間。実際、ほとんどの場合シグナルは生成されませんが、ticks 関数は常に実行されます。サーバー プログラムが 1 秒間に 1,000 リクエストを受信した場合、各リクエストは平均して 1,000 行の PHP コードを実行することになります。次に、PHP の pcntl_signal により、さらに 1000 * 1000、つまり 100 万回の空の関数呼び出しが発生します。これにより、多くの CPU リソースが無駄になります。より良いアプローチは、ティックを削除し、代わりに pcntl_signal_dispatch を使用してコード ループ内でシグナルを自分で処理することです。 pcntl_signal_dispatch 関数の実装:

void pcntl_signal_dispatch()
{
 //.... 这里略去一部分代码,queue即是信号队列
 while (queue) {
  if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) {
   ZVAL_NULL(&retval);
   ZVAL_LONG(&param, queue->signo);

   /* Call php signal handler - Note that we do not report errors, and we ignore the return value */
   /* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
   call_user_function(EG(function_table), NULL, handle, &retval, 1, &param TSRMLS_CC);
   zval_ptr_dtor(&param);
   zval_ptr_dtor(&retval);
  }
  next = queue->next;
  queue->next = PCNTL_G(spares);
  PCNTL_G(spares) = queue;
  queue = next;
 }
}

しかし、上記のようなものには、無限ループに入れなければならないという嫌な部分もあります。 PHP7.1以降、非同期シグナルの受信と処理を完了する関数が登場しました。 pcntl_async_signals

<?php
//a.php
echo getmypid();
pcntl_async_signals(true);//开启异步监听信号
pcntl_signal(SIGUSR1,function(){
    echo "触发信号";
    posix_kill(getmypid(),SIGSTOP);
});
posix_kill(getmypid(),SIGSTOP);//给进程发送暂停信号

//b.php
posix_kill(文件1进程, SIGCONT);//给进程发送继续信号
posix_kill(文件1进程, SIGUSR1);//给进程发送user1信号

pcntl_async_signals メソッドを使用すると、無限ループを記述する必要はありません。

监听信号的包:

https://github.com/Rain-Life/monitorSignal

以上がPHP プロセスの信号処理を 1 つの記事で徹底的に理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。