PHP の真のマルチプロセス動作モードを使用し、データ収集、大量メール送信、データ ソースの更新、TCP サーバーなどに適しています。
PHP にはプロセス制御関数のセットがあり (コンパイル時に -enable-pcntl と posix 拡張子が必要です)、これにより PHP は子プロセスを作成し、exec 関数を使用してプログラムを実行し、C などの *nix システムでシグナルを処理できます。 。 PCNTL は、シグナル ハンドル コールバック メカニズムとしてティックを使用します。これにより、非同期イベントを処理する際の負荷を最小限に抑えることができます。ダニとは何ですか? Tick は、インタープリタがコード セグメント内で N 個の低レベル ステートメントを実行するたびに発生するイベントです。このコード セグメントは宣言によって指定する必要があります。
一般的に使用される PCNTL 関数
1.pcntl_alarm (int $秒)
$秒秒後に SIGALRM シグナルを送信するようにカウンターを設定します
2. pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] )
シグナルを処理するために $signo のコールバック関数を設定します。以下は、SIGALRM シグナルを 5 秒ごとに送信し、signal_handler 関数で取得して、「Caught SIGALRM」を出力する例です。
宣言(ティック = 1);
関数 signal_handler($signal) {
「SIGALRMn をキャッチしました」を印刷します。
pcntl_alarm(5);
}
pcntl_signal(SIGALRM, “signal_handler”, true);
pcntl_alarm(5);
のため(;;) {
}
?>
c の exec ファミリ関数と同様に、現在のプロセス空間で指定されたプログラムを実行します。いわゆるカレントスペースとは、指定されたプログラムのコードがロードされ、プログラムの実行後にプロセスが上書きされるスペースです。
$dir = '/ホーム/シャンカ/'; $cmd = 'ls'; $オプション = '-l'; $pathtobin = '/bin/ls';
$arg = 配列($cmd, $option, $dir);
pcntl_exec($pathtobin, $arg);
echo '123' //この行は実行されません
?>
現在のプロセスの子プロセスを作成し、最初に親プロセスを実行します。返されるのは子プロセスの PID であり、0 より大きい必要があります。親プロセスのコードで、pcntl_wait (&$status) を使用すると、子プロセスが戻り値を取得するまで親プロセスを一時停止できます。注: 親プロセスをブロックすると、子プロセスもブロックされます。ただし、親プロセスの終了は子プロセスの動作には影響しません。
親プロセスの実行が終了すると、子プロセスが実行されます。このとき、子プロセスはpcntl_fork()を実行する文(この関数を含む)から実行を開始しますが、この時点ではゼロを返します(つまり、これが子プロセスであることを示します)。子プロセスのコード ブロックに exit ステートメントを含めるのが最善です。つまり、子プロセスの実行後すぐに終了します。そうしないと、スクリプトの一部の実行が再度開始されます。
1. 不要なエラーを防ぐために、子プロセスに exit; ステートメントを含めるのが最善です。 2. pcntl_fork の間に次のような他のステートメントを入れないことをお勧めします。
$pid = pcntl_fork(); //ここには他のステートメントを入れない方が良いですif ($pid == -1) {
die('フォークできませんでした'); } else if ($pid) {// 私たちは親です
pcntl_wait($status); //子供をゾンビから守る
} その他 {
// 私たちは子供です
}
5. pcntl_wait ( int &$status [, int $options ] )
現在のプロセスの子プロセスが終了するか、現在のプロセスを終了するシグナルを受信するまで、現在のプロセスをブロックします。 $status を使用して子プロセスのステータス コードを返します。2 番目のパラメーターを指定して、ブロッキング状態で呼び出すかどうかを指定できます:
1. ブロッキングモードで呼び出された場合、関数の戻り値は子プロセスの pid です。子プロセスがない場合、戻り値は -1 です。
2. 非ブロッキング方式で呼び出されたこの関数は、子プロセスが実行中であるが終了していない場合に 0 を返すこともあります。
6. pcntl_waitpid ( int $pid , int &$status [, int $options ] )
関数は pcntl_wait と同じですが、異なる点は、waitpid が指定された pid を待機する子プロセスであることです。 pid が -1 の場合、pcntl_waitpid は pcntl_wait と同じになります。子プロセスのステータス情報は、pcntl_wait および pcntl_waitpid 関数の $status に保存されます。このパラメーターは、pcntl_wifexited、pcntl_wifstopped、pcntl_wifsignaled、pcntl_wexitstatus、pcntl_wtermsig、pcntl_wstopsig、pcntl_waitpid などの関数で使用できます。
例:
$pid = pcntl_fork(); if($pid) {
pcntl_wait($ステータス); $id = getmypid(); echo 「親プロセス、pid {$id}、子 pid {$pid}n」; }その他{
$id = getmypid();
echo "子プロセス,pid {$id}n";
睡眠(2);
}
?>
子プロセスなどを出力した後のスリーププロセスは2秒後に終了し、親プロセスは子プロセスが終了するまでプロセスをブロックしました。
7. pcntl_getpriority ([ int $pid [, int $process_identifier ]] )
8. pcntl_setpriority ( int $priority [, int $pid [, int $process_identifier ]] )
9.posix_kill
10.pcntl_singal
親プロセスが終了すると、子プロセスは親プロセスの終了をどのようにして知るのでしょうか? 親プロセスが終了すると、子プロセスは通常、次の 2 つの比較的単純な方法で親プロセスが終了したことを知ることができます:
1. 親プロセスが終了すると、子プロセスを引き継ぐための INIT プロセスが存在します。この INIT プロセスのプロセス番号は 1 であるため、子プロセスは getppid() を使用して現在の親プロセスの pid を取得できます。 1 が返された場合は、親プロセスが INIT プロセスとなり、元のプロセスが起動されたことを示します。
上記の 2 つの方法に加えて、リアルタイム監視のためのパイプラインやソケットの確立など、より複雑な実装方法もいくつかあります。
PHPマルチプロセスデータ収集の例
/**
* プロジェクト: Signfork: php マルチスレッド ライブラリ
* ファイル: Signfork.class.php
*/
クラスサインフォーク{
* サブプロセス通信ファイルが配置されるディレクトリを設定します
* @var 文字列
*/
プライベート $tmp_path='/tmp/';
/**
* 1. $arg の型を決定します。型が配列の場合、値は各子プロセスに渡されます。型が数値型の場合、作成されるプロセスの数を表します。
* @param オブジェクト $obj 実行オブジェクト
* @param string|array $arg は、オブジェクト内の __fork メソッドによって実行されるパラメータに使用されます
* 例: $arg、自動的に分解: $obj->__fork($arg[0])、$obj->__fork($arg[1])…
* @return array 戻り配列(サブプロセスシーケンス=>サブプロセス実行結果);
*/
パブリック関数 run($obj,$arg=1){
if(!method_exists($obj,'__fork')){
exit("メソッド '__fork' が見つかりません!");
}
if(is_array($arg)){
$i=0;
$spawns[$i]=$key;
$i++;
$this->spawn($obj,$key,$val);
}
$spawns['total']=$i;
}elseif($spawns=intval($arg)){
for($i = 0; $i
$this->spawn($obj,$i);
}
}その他{
exit('議論が間違っています!');
}
if($i>1000) exit('スポーンが多すぎます!');
return $this->request($spawns);
/**
*Signforkのメインプロセス制御方法
* 2. $data はサブプロセスの実行結果とデータを収集し、最終的な返却に使用します
* 3. 子プロセスファイルを削除
* 4. すべてのサブプロセスが実行され、サブプロセスのリソースがクリーンアップされるまで、0.03 秒間 1 回ポーリングします
* @param string|array $arg は各子プロセスの ID に対応するために使用されます
* @return array return array([サブプロセスシーケンス]=>[サブプロセス実行結果]);
*/
プライベート関数リクエスト($spawns){
$data=array();
$i=is_array($spawns)?$spawns['total']:$spawns;
for($ids = 0; $ids
while(!($cid=pcntl_waitpid(-1, $status, WNOHANG)))usleep(30000);
$tmpfile=$this->tmp_path.'sfpid_'.$cid;
$data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile);
リンク解除($tmpfile);
}
$data を返します;
}
/**
* Signforkサブプロセス実行方法
* 2. file_put_contentsは、「$obj->__fork($val)」の実行結果を特定の順序で名前を付けたテキストに格納します
* 3. posix_kill は現在のプロセスを強制終了します
* @param object $obj 実行されるオブジェクト
* @param object $i 各子プロセスに対応するデータを返すための子プロセスのシーケンスID
* @param オブジェクト $param は、オブジェクト $obj メソッド '__fork' 実行パラメータを入力するために使用されます
*/
プライベート関数 spawn($obj,$i,$param=null){
if(pcntl_fork()===0){
$cid=getmypid();
file_put_contents($this->tmp_path.'sfpid_'.$cid,$obj->__fork($param));
posix_kill($cid, SIGTERM);
終了します;
}
}
}
?>
pcntl_fork() の後に php によって生成された子プロセス (通常はゾンビプロセス) は、pcntl_waitpid() 関数によってリソースを解放する必要があります。ただし、pcntl_waitpid() で解放されるのは、必ずしも現在実行中のプロセスであるとは限りません。過去に生成された (解放されていない) ゾンビ プロセスである可能性もあり、同時実行中に他の訪問者のゾンビ プロセスである可能性もあります。ただし、posix_kill($cid, SIGTERM) を使用すると、終了時に子プロセスを強制終了できます。
子プロセスは、変数を親プロセス空間に自動的にコピーします。
PHPマルチプロセスプログラミング例2
//…..
//pcntl の php 拡張機能をインストールしてロードする必要があります
if(function_exists(“pcntl_fork”)){
//子プロセスを生成します
$pid = pcntl_fork();
if($pid == -1){
die('フォークできませんでした');
}その他{
if($pid){
$ステータス = 0;
// 子プロセスが終了するまで親プロセスをブロックします。長時間実行する必要があるスクリプトには適していません。非ブロッキングを実現するには、pcntl_wait($status, 0) を使用します。
pcntl_wait($status);
// 親の proc コード
終了します;
}その他{
// 子プロシージャコード
//ゾンビプロセスの生成を防ぐために現在の子プロセスを終了します
if(function_exists(“posix_kill”)){
posix_kill(getmypid(), SIGTERM);
}その他{
system('kill -9'.getmypid());
}
終了します;
}
}
}その他{
//マルチプロセス処理をサポートしていない場合のコードはこちら
}
//…..
?>
pcntl_wait($status, 1);
//または
pcntl_wait($status, WNOHANG);
上記のコードでは、親プロセスが終了すると(exit関数を使用して終了またはリダイレクト)、子プロセスがゾンビプロセスになり(制御のためにinitプロセスに渡されます)、子プロセスはは実行されなくなります。
ゾンビプロセスとは、親プロセスが終了したことを意味し、終了後にプロセスを受け入れるプロセスがない場合、そのプロセスはゾンビプロセスになります。どのプロセスも(exit を使用して)終了する前にゾンビ プロセス(プロセスのステータスやその他の情報を保存するために使用)になり、その後 init プロセスが引き継ぎます。ゾンビ プロセスが時間内にリサイクルされないと、システム内のプロセス テーブル エントリが占有されてしまい、そのようなゾンビ プロセスが多すぎると、最終的にシステムに使用可能なプロセス テーブル エントリがなくなり、実行できなくなります。他のプログラム。
ゾンビプロセスを防ぐ方法はいくつかあります:
1. 親プロセスは、wait や waitpid などの関数を使用して、子プロセスが終了するのを待ってから親プロセスでコードを実行します。これにより、親プロセスがハングします。上記のコードはこのように実装していますが、WEB環境では子プロセスを長時間実行する必要がある(タイムアウトが発生する)場合には適していません。
wait メソッドと waitpid メソッドを使用して、親プロセスがそのゾンビ子プロセスを自動的にリサイクルします (子プロセスの戻りステータスに従って) waitpid は指定された子プロセスを一時的に制御するために使用され、wait はすべての子プロセスに対して行われます。
2. 親プロセスが非常にビジーな場合は、シグナル関数を使用して SIGCHLD のハンドラーをインストールできます。これは、子プロセスが終了した後、親プロセスがシグナルを受信し、ハンドラー内で wait を呼び出してリサイクルできるためです
3. 親プロセスが子プロセスの終了を気にしない場合は、シグナル (SIGCHLD、SIG_IGN) を使用して、子プロセスの終了に関心がないことをカーネルに通知します。その後、子プロセスが終了した後、カーネルが通知します。これはリサイクルされ、親プロセスには渡されなくなります。たとえば、次のようなシグナルを送信します。
pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
4. もう 1 つの方法は、親プロセスが子プロセスをフォークし、その後、子プロセスが終了した後に終了することです。 、init はそれをリサイクルします。ただし、子プロセスのリサイクルは自分で行う必要があります。以下に例を示します:
#含める
int main(void){
pid_t pid;
if ((pid = fork())
err_sys(“フォークエラー”);
if ((pid = fork())
err_sys(“フォークエラー”);
}elseif(pid > 0){
終了(0); /**//* 2 番目のフォークの親 == 最初の子*/
}
/**
* 私たちは 2 番目の子供です。私たちの親はすぐに init になります
* ここでは、いつ実行されるかを知って実行を継続します
* これで完了です。init がステータスを獲得します。
*/
睡眠(2);
printf(“2 番目の子、親 pid = %d “, getppid());
終了(0);
}
if (waitpid(pid, NULL, 0) != pid) /**//* 最初の子を待ちます*/
err_sys(“waitpid エラー”);
/**
* 私たちは親 (元のプロセス) です。実行を続けます
*/
終了(0);
}
fork()/execve() プロセスでは、子プロセスの終了時に親プロセスがまだ存在しているものとみなされ、親プロセスは fork() の前に SIGCHLD 信号処理関数をインストールしていないため、waitpid() を呼び出し、待機します。このシグナルが発生すると、子プロセスはゾンビ プロセスになり、現時点では root として kill-9 を実行してもゾンビ プロセスを強制終了できません。解決策は、ゾンビ プロセスの親プロセスを強制終了することです (ゾンビ プロセスの親プロセスが存在する必要があります)。ゾンビ プロセスは「孤立プロセス」になり、init が定期的に wait を呼び出します。親プロセスが終了した子ゾンビをリサイクルしてクリーンアップします。
したがって、上記の例は次のように変更できます:
//…..
//pcntl の php 拡張機能をインストールしてロードする必要があります
if(function_exists(“pcntl_fork”)){
//最初の子プロセスを生成します
$pid = pcntl_fork(); //$pid は生成された子プロセス ID です
if($pid == -1){
//サブプロセスフォークが失敗しました
die('フォークできませんでした');
}その他{
if($pid){
//親プロセスのコード
sleep(5); //5秒待ちます
exit(0); //または $this->_redirect('/');
}その他{
//最初の子プロセスコード
//孫プロセスを生成
if(($gpid = pcntl_fork())
//Sun プロセスの生成に失敗しました
die('フォークできませんでした');
}elseif($GPID > 0){
//孫プロセスの親プロセスである最初の子プロセスのコード
$ステータス = 0;
$status = pcntl_wait($status); //子プロセスをブロックし、孫プロセスの終了ステータスを返し、正常に終了したかどうかを確認します
if($status ! = 0) file_put_content('ファイル名', 'Sun プロセスが異常終了しました');
//親プロセスIDを取得します
//$ppid = posix_getppid(); //$ppid が 1 の場合、親プロセスが init プロセスになり、元の親プロセスが終了したことを意味します
//子プロセス ID を取得します: posix_getpid() または getmypid() または fork によって返された変数 $pid
//子プロセスを強制終了します
//posix_kill(getmypid(), SIGTERM);
終了(0);
}else{ //それは $GPID == 0 です
//Sun プロセス コード
//….
//ゾンビプロセスの生成を防ぐために孫プロセス(つまり、現在のプロセス)を終了します
if(function_exists('posix_kill')){
posix_kill(getmypid(), SIGTERM);
}その他{
system('kill -9'.getmypid());
}
終了(0);
}
}
}
}その他{
//マルチプロセス処理をサポートしていない場合のコードはこちら
}
//…..
?>
ゾンビプロセスの生成方法
プロセスがその寿命を終了するために exit コマンドを呼び出した場合、そのプロセスは実際には破棄されず、ゾンビ プロセス (Zombie) と呼ばれるデータ構造を残します (システムは exit を呼び出し、その機能はプロセスを終了させることですが、それは制限されているだけです)通常のプロセスをゾンビ プロセスに変えることはできませんが、完全に破壊することはできません)。 Linux プロセスのステータスの中でも、ゾンビ プロセスは非常に特殊な種類であり、ほとんどすべてのメモリ領域を放棄しており、実行可能コードを持たず、終了を記録するためにプロセス リスト内の位置を保持するだけです。さらに、ゾンビ プロセスはメモリ領域を占有しなくなります。親プロセスが SIGCHLD シグナル処理関数をインストールせず、wait または waitpid() を呼び出して子プロセスが終了するのを待ち、シグナルを明示的に無視しない場合、親プロセスはそのまま残ります。ゾンビ状態の場合、この時点で親プロセスが終了すると、init プロセスが自動的に子プロセスを引き継ぎ、その死体を回収しますが、それでもクリアできます。ただし、親プロセスがループで終了しない場合、子プロセスはゾンビ状態のままになるため、システム内に多くのゾンビ プロセスが存在することがあります。
子プロセス (init を除く) は、exit() の直後には消えませんが、ゾンビ プロセス (Zombie) と呼ばれるデータ構造を残して、親プロセスの処理を待ちます。これは、すべての子プロセスが最後に通過する段階です。子プロセスが exit() の後に処理する時間がない場合は、ps コマンドを使用して、子プロセスのステータスが「Z」であることを確認できます。親プロセスが時間内にそれを処理できる場合、ps コマンドを使用して子プロセスのゾンビ状態を確認するには手遅れになる可能性がありますが、これは子プロセスがゾンビ状態を経ないという意味ではありません。
子プロセスが終了する前に親プロセスが終了した場合、子プロセスは init によって引き継がれます。 initはゾンビ状態の子プロセスを親プロセスとして処理します。
さらに、php ファイルを作成してバックグラウンドで実行することもできます。例:
//アクションコード
パブリック関数 createAction(){
//….
// args を insertLargeData.php に渡すパラメーターに置き換え、パラメーターをスペースで区切ります
system('php -f insertLargeData.php ' . ' args ' . '&');
$this->リダイレクト('/');
}
?>
次に、insertLargeData.php ファイルでデータベース操作を実行します。 cronjob + php を使用して大量のデータを処理することもできます。
ターミナルで php コマンドを実行する場合、ターミナルが閉じられると、実行したばかりのコマンドも強制的に閉じられます。これを実現するには、nohup コマンドを使用します。
//アクションコード
パブリック関数 createAction(){
//….
// args を insertLargeData.php に渡すパラメータに置き換え、パラメータをスペースで区切ります
system('nohup php -f insertLargeData.php ' . ' args ' . '&');
$this->リダイレクト('/');
}
?>
nohup コマンドの代わりに screen コマンドを使用することもできます。
)