ホームページ >バックエンド開発 >PHPチュートリアル >PHP マルチプロセス プログラミングの予備的な入門
この記事では、主に PHP マルチプロセス プログラミングの初期入門を紹介します。一定の参考値があります。今、みんなに共有しています。困っている友達は参考にしてください。
ナルト シャドウでナルトを羨むクローン?はい、PHP プログラムはシャドウ クローンをアクティブ化できます。タスクを完了したいが、1 つのプロセスでは遅すぎると感じる場合は、複数のプロセスを使用してみてください。この記事では、PHP マルチプロセスの基本的なニーズ、マルチプロセスの作成方法、および基本的なシグナル制御について説明しますが、プロセス間の通信および情報共有の方法については当面説明しません。
始める前に、M$ Windows プラットフォームを使用していないことを確認してください (私は M$ Windows プラットフォームを使用していないため)ウィンドウズ)。 Linux/BSD/Unix は問題ありません。動作環境を確認したら、必要なPHPモジュールが利用可能か確認してみましょう。ターミナルを開き、次のコマンドを入力します:
$ php -m
このコマンドは、現在有効な PHP の拡張機能をすべてチェックして出力します。出力に pcntl
と posix
が含まれているかどうかを確認します。リスト。
pcntl
が見つからない場合は、この拡張機能がコンパイル中にコンパイルされなかった可能性があります。私と同じように PHP をコンパイルしてインストールした場合は、PHP を再コンパイルしてインストールする必要があります。構成時に --enable-pcntl
パラメータを忘れずに追加してください。
$ cd /path/to/php_source_code_dir $ ./configure [some other options] --enable-pcntl $ make && make install
この製品は、コンパイル時に --disable-posix
を追加しない限り、通常はデフォルトでインストールされます。
続行する前に、Linux マルチプロセスについて少し理解しておく必要があります。マルチプロセッシングでは何が起こっているのでしょうか?これはNARUTOのシャドウクローンとは少し異なります。まず、ナルトは16歳のような幼い頃から成長しました。ある日、彼はシャドウクローンを起動し、自分自身を5つに分割しました。明らかに、これらのクローンも16歳のナルトであり、生まれたときに何も理解せずに泣いている赤ん坊ではありません(これをクローンと呼びます)。そして、ここで違いが生じます:クローンは自分のことをする独立した人間になり、他のクローンや元の体が何をしたかはもうわかりません(もちろん、漫画のように経験を積むことはありません)元の人間にとって) 。コミュニケーションを取らない限り、共通の記憶となるのは16歳以前のことだけ。
クラスメートの中には、「ボス、浮気してるんじゃないの?」と言う人もいました。ナルト見てないです!それからご覧ください...
最後に、準備知識は終わりました。メイン プロセスによって作成される子プロセスがどのようなものであるかを一般的に理解するだけです。子プロセスのコードはメインプロセスとまったく同じで、同じものの他の部分はシャドウクローンが起動されるまでに実行されるすべてのものです。詳細については、「オペレーティング システム」コースを参照してください。
それでは、基本的な知識がなければ、どうやって巻物の内容を理解できるのでしょうか?巻物を開いたとき、最初にフォークという単語が目に入りました。
フォーク?フォークが二股に分かれていて、1本が複数になっています!まさにそれが意味するところだ。このコマンドを使用して子プロセスを作成します。ここでは pcntl_fork()
関数を使用する必要があります。 (まず、PHP マニュアルでこの関数の概要を簡単に見てみることができます。) PHP スクリプトを作成します:
$pid = pcntl_fork(); // 一旦调用成功,事情就变得有些不同了 if ($pid == -1) { die('fork failed'); } else if ($pid == 0) {} else {}
pcntl_fork()
この関数は子プロセスを作成します。子プロセスと親プロセスの違い PID(プロセスID)とPPID(親プロセスID)が異なります。ターミナルでプロセスを表示するには、ps
コマンドを使用します (ps の使用方法を man に尋ねます: man ps
)。関数が -1 を返した場合、フォークが失敗したことを意味します。 if
の前に文を追加してみてください: echo $pid . PHP_EOL;
。スクリプトを実行すると、出力は次のようになります (結果は、子プロセスのコードと親プロセスのコードが同じであることを示しています):
67789 # 这个是父进程打印的 0 # 这个是子进程打印的
pcntl_fork()
関数の後呼び出しが成功した場合、親プロセスでは子プロセスの PID が返され、子プロセスでは 0 が返されます。したがって、if
ブランチは、親プロセスと子プロセスが異なることを行うように制御するために、そのすぐ下で使用されます。
それでは、ナルトが 16 歳のときのシャドウ クローンについて説明し、2 つの単純な出力タスクを元の本体とクローンに割り当てます:
$parentPid = getmypid(); // 这就是传说中16岁之前的记忆 $pid = pcntl_fork(); // 一旦调用成功,事情就变得有些不同了 if ($pid == -1) { die('fork failed'); } else if ($pid == 0) { $mypid = getmypid(); // 用getmypid()函数获取当前进程的PID echo 'I am child process. My PID is ' . $mypid . ' and my father's PID is ' . $parentPid . PHP_EOL; } else { echo 'Oh my god! I am a father now! My child's PID is ' . $pid . ' and mine is ' . $parentPid . PHP_EOL;}
出力結果は次のようになります:
Oh my god! I am a father now! My child's PID is 68066 and mine is 68065 I am child process. My PID is 68066 and my father's PID is 68065
もう一度強調します。 pcntl_fork()
呼び出しが成功すると、1 つのプログラムが 2 つのプログラムになります。1 つのプログラムが を取得します。 $pid
変数の値は 0 (子プロセスです)、別のプログラムによって取得された $pid
の値は 0 より大きくなります。この値は子プロセスの PID です。は親プロセスです。以下の分岐ステートメントでは、$pid
値が異なるため、異なるコードが実行されます。もう一度強調しておきますが、子プロセスのコードは親プロセスのコードと同じです。したがって、分岐ステートメントを通じて異なるタスクをそれらに割り当てる必要があります。
刚刚有man ps
么?一般我习惯用ps aux
加上grep
命令来查找运行着的后台进程。其中有一列STAT
,标识了每个进程的运行状态。这里,我们关注状态Z
:僵尸(Zombie)。当子进程比父进程先退出,而父进程没对其做任何处理的时候,子进程将会变成僵尸进程。Oops,又跟火影里的影分身不一样了。鸣人的影分身被干死了以后就自动消失了,但是这里的子进程分身死了话还留着一个空壳在,直到父进程回收它。僵尸进程虽然不占什么内存,但是很碍眼,院子里一堆躺着的僵尸怎么都觉得怪怪的。(别忘了它们还占用着PID)
一般来说,在父进程结束之前回收挂掉的子进程就可以了。在pcntl
扩展里面有一个pcntl_wait()
函数,它会将父进程挂起,直到有一个子进程退出为止。如果有一个子进程变成了僵尸的话,它会立即返回。所有的子进程都要回收,所以多等等也没关系啦!
如果父进程先挂了怎么办?会发生什么?什么也不会发生,子进程依旧还在运行。但是这个时候,子进程会被交给1号进程,1号进程成为了这些子进程的继父。1号进程会很好地处理这些进程的资源,当它们结束时1号进程会自动回收资源。所以,另一种处理僵尸进程的临时办法是关闭它们的父进程。
一般多进程的事儿讲到上面就完了,可是信号在系统中确实是一个非常重要的东西。信号就是信号灯,点亮一个信号灯,程序就会做出反应。这个你一定用过,比如说在终端下运行某个程序,等了半天也没什么反应,可能你会按 Ctrl+C 来关闭这个程序。实际上,这里就是通过键盘向程序发送了一个中断的信号:SIGINT。有时候进程失去响应了还会执行kill [PID]
命令,未加任何其他参数的话,程序会接收到一个SIGTERM信号。程序收到上面两个信号的时候,默认都会结束执行,那么是否有可能改变这种默认行为呢?必须能啊!
人是活的程序也是活的,只不过程序需要遵循人制定的规则来运行。现在开始给信号重新设定规则,这里用到的函数是pcntl_signal()
(继续之前为啥不先查查PHP手册呢?)。下面这段程序将给SIGINT重新定义行为,注意看好:
// 定义一个处理器,接收到SIGINT信号后只输出一行信息 function signalHandler($signal) { if ($signal == SIGINT) { echo 'signal received' . PHP_EOL; }} // 信号注册:当接收到SIGINT信号时,调用signalHandler()函数pcntl_signal(SIGINT, 'signalHandler');while (true) { sleep(1); // do something pcntl_signal_dispatch(); // 接收到信号时,调用注册的signalHandler()}
执行一下,随时按下 Ctrl+C 看看会发生什么事。
说明一下:pcntl_signal()
函数仅仅是注册信号和它的处理方法,真正接收到信号并调用其处理方法的是pcntl_signal_dispatch()
函数。试试把// do something
替换成下面这段代码:
for ($i = 0; $i < 1000000; $i++) { echo $i . PHP_EOL; usleep(100000);}
在终端下执行这个脚本,当它不停输出数字的时候尝试按下 Ctrl+C 。看看程序有什么响应?嗯……什么都没有,除了屏幕可能多了个^C
以外,程序一直在不停地输出数字。因为程序一直没有执行到pcntl_signal_dispatch()
,所以就并没有调用signalHandler()
,所以就没有输出signal received
。
如果认真看了PHP文档,会发现pcntl_signal_dispatch()
这个函数是PHP 5.3以上才支持的,如果你的PHP版本大于5.3,建议使用这个方法调用信号处理器。5.3以下的版本需要在注册信号之前加一句:declare(ticks = 1);
表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。想想就挺不爽的,干嘛一直都检查?还是在我们指定的地方检查一下就好。
现在我们回到子进程回收的问题上(差点忘了= =")。当你的一个子进程挂了(或者说是结束了),但是父进程还在运行中并且可能很长一段时间不会退出。一个僵尸进程从此站起来了!这时,保护伞公司(内核)发现它的地盘里出现了一个僵尸,这个僵尸是谁儿子呢?看一下PPID就知道了。然后,内核给PPID这个进程(也就是僵尸进程的父进程)发送一个信号:SIGCHLD。然后,你知道怎么在父进程中回收这个子进程了么?提示一下,用pcntl_wait()
函数。
今、慎重に kill
コマンドを実行していただければ幸いです。実際にはプロセスにシグナルを送信します。PHP では、posix_kill()
関数を呼び出して同じ効果を実現することもできます。これを使用すると、親プロセス内の他の子プロセスの実行を制御できます。たとえば、親プロセスが終了する前にすべての子プロセスを閉じ、fork
のときに親プロセス内のすべての子プロセスの PID を記録し、親プロセスが終了する前に子プロセスに順番に終了シグナルを送信します。 。
PHP のマルチプロセスは C と非常によく似ており、一度理解すれば、他の言語で書いても同様になります。時間があれば、小さなプログラムを書いて、それを個人的に体験してみてください。
16 歳のナルトがシャドウ クローンを送信し、5 つのクローンに分割されました
各クローンは 10 ~ 30 秒間ランダムに生存し、毎秒何かを出力します。
元のクローンがクローンの終わりを感じられることを確認してから、別のクローンをアクティブにします。クローンが最大 5 つあることを確認してください
nohup
は使用しないでください。これにより、ターミナルを閉じた後も元の本体が実行できるようになります
クローンの数 (5) を構成ファイルに書き込みます。元の本体にシグナルを送信するとき (SIGUSR1 または SIGUSR2 の使用を検討してください)、元の本体は構成ファイルを読み取り、許可されるクローンの最大数を更新します。
クローンが多すぎる場合は、いくつかを閉じます。クローンが少ない場合は、さらにいくつかを分割します。
ヒント:
#whileループを使用してプロセスが実行されていることを確認し、##sleep
に注意して CPU 使用率が 100% にならないようにします
PHP ポリモーフィズムの理解
以上がPHP マルチプロセス プログラミングの予備的な入門の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。