PHP は、pcntl 拡張機能を使用してマルチプロセス プログラミングを実装できます。pcntl を使用してマルチプロセスを作成する方法に関するオンライン情報については、ここでは説明しません。主に、pcntl_fork の落とし穴と、関連するいくつかの比較的珍しい関数の使用について説明します。 、これも穴を掘って埋めることから導き出された結論です。
くだらないことは言わないで、とにかく始めてください
実際に、PHP を使用したマルチプロセスの練習用のモデルは、おおよそ次のとおりです。各子プロセスが対応するファイルを作成し、最後に親プロセスが作成できることを期待しています。親プロセスのファイルを作成します。コードは次のとおりです (落とし穴あり):
$pid_dir = __dir__."/pid_files";for($i=0; $i<3; $i++){ $pid = pcntl_fork(); if($pid == -1){ var_dump("fork failed"); } if(!$pid){ //子进程代码 $pid = posix_getpid(); $ppid = posix_getppid(); $r = rand(0,100); //随机数 touch("$pid_dir/fork_child_process_{$i}_{$ppid}_{$pid}_{$r}"); }} $pid = posix_getpid();$ppid = posix_getppid();$r = rand(0,100); //随机数touch("$pid_dir/fork_process_pid_{$ppid}_{$pid}_$r");
上記のコードでは、ループを通じて 3 つの子プロセスを作成し、各プロセスがファイルを作成し、完了後にファイルを作成します。 、親プロセスは独自のファイルを作成するため、最終的には 4 つのファイルが作成されるはずですが、これは当てはまりません:
fork_child_process_0_62656_62658_39fork_child_process_1_62656_62659_51fork_child_process_1_62658_62660_22fork_child_process_2_62656_62661_91fork_child_process_2_62658_62662_22fork_child_process_2_62659_62663_82fork_child_process_2_62660_62664_59fork_process_pid_62225_62656_48fork_process_pid_62656_62658_22fork_process_pid_62656_62659_82fork_process_pid_62656_62661_65fork_process_pid_62658_62660_59fork_process_pid_62658_62662_59fork_process_pid_62659_62663_61fork_process_pid_62660_62664_10
上記の結果が発生する理由は、fork 後に元のプロセスが1 つのメインプロセスと 1 つの子プロセスの 2 つのプロセスに分割されます。fork の後のコードはすべて共有されますが、fork の戻り値を使用して、対応するサブプロセスを実行するかどうかを判断できます。メインプロセスのロジックですが、サブプロセス自体は for ループ部分に進みます。サブプロセスは自分で子プロセスを作成したので、上に複数の child_process ファイルがあるのはなぜなのか、それを分析してみましょう。
ループ変数 $i、$i が 0 の場合、メインプロセス a (変更なし) と子プロセス aa が生成され、子プロセス ファイル fork_child_process_0_62656_62658_39、メイン プロセスが作成されます。 a は $i=1 で循環し続け、子プロセス ab が作成され、fork_child_process_1_62656_62659_51 が作成されます。メイン プロセス a は $i=2 で循環し続け、子プロセス ac が作成されます。 fork_child_process_2_62656_62661_91 は、メイン プロセス a の pid です。
この時点で、 aa によって作成された最初のサブプロセス aa を見てみましょう。上記のサブプロセスファイルが作成された後は何も起こりません。そうしないと、引き続き for ループが実行され、メインプロセス a のループ変数、つまり の値が継承されます。 $i が 0 であるため、aa プロセスの次のループの $i は 1 となり、aa は作成を続行します。子プロセス aaa はファイル fork_child_process_1_62658_62660_22 を作成します。aa は $i=2 として子プロセス aab を作成します。この子プロセスは、fork_child_process_2_62658_62662_22 ファイルを作成します。ここで、aaa と aab の ppid が aa 62658 であることがわかります。このとき、aaa と aab も、aa の $i 値を継承するのが妥当です。 $i の値は 1 です。ループが継続すると、$i は 2 になります。これは、aaa と aab が子プロセス ファイル fork_child_process_2_62659_62663_82(aaaa)、およびそれらに対応する親を作成することを意味します。プロセスは aaa(62659) と aab(62660) です。
この時点で、for ループ内のマルチプロセス ロジックが完了しているため、最初の部分は 7 つのファイルが生成されました
として2 番目の部分に 8 つのファイルがある理由については、自分で考えてみてください。メイン プロセスとサブプロセスに関係なく、for ループが完了した後は処理が継続されることに注意してください。理解する。
実際のコードでは、この間違いを犯しました。
上記の問題を解決するには、子プロセスの実行の最後に終了するだけです
fork_child_process_0_63219_63221_66fork_child_process_1_63219_63222_88fork_child_process_2_63219_63223_22fork_process_pid_62225_63219_77
続けて、インターネットでマルチプロセスプログラミングで使用されているpcntl_waitpidをたくさん見たのですが、意味がわかりません。例は非常に少ないので、今のところ私の理解について話しましょう
fork の子プロセスのステータスを待つか返します。
int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )
返される値は、-1、0、または >0 の値になります。 - 1、子プロセスにエラーがあることを示します。>0 は子プロセスが終了したことを示し、値が終了する子プロセスの PID である場合は、$status ステータス コードを使用して応答できます。 では、いつ 0 が返されるのでしょうか? オプション パラメータが WNOHANG で、子プロセスが実行中の場合、つまり、options=WNOHANG が設定されている場合、子プロセスが終了していない場合、pcntl_waitpid は 0 を返します
Inさらに、このパラメータが WNOHANG に設定されていない場合、pcntl_waitpid は子プロセスが終了するまで操作をブロックします。オプションのもう 1 つの値である WUNTRACED については、まだ理解されておらず、表現できません
それでは、どのように決定するかです。プロセスは次のように $status (ステータス コード) に基づいて終了しました (パラメーターはすべて $status です)
pcntl_wifexited この関数は、$status に基づいてプロセスが正常に終了したかどうか、正常終了とは何かを判断できます。出口
这个函数仅在pcntl_wifexited 返回True(即正常退出)时有效, 且返回子进程退出的返回状态码, 这个返回状态码可以通过exit($s)的参数($s必须为整数时)定义
检查子进程状态码是否代表由于某个信号而中断, 比如是不是我们给他发送了term, int 等信号了
假如是发送信号而导致子进程中断, 那么这个信号是什么信号呢, 这个函数就是获取这个信号的
仅当option选项为WUNTRACED时有效, 未理解, 不表
同上
综合实例代码:
$res = pcntl_waitpid($pid, $status, WNOHANG);//FileLog::log("pid is $pid; wait result is $res");if($res == -1 || $res > 0){ if(!pcntl_wifexited($status)){ //进程非正常退出 FileLog::log("service stop unusally; pid is $pid"); }else{ //获取进程终端的退出状态码; $code = pcntl_wexitstatus($status); FileLog::log("service stop code: $code;pid is $pid "); } if(pcntl_wifsignaled($status)){ //不是通过接受信号中断 FileLog::log("service stop not by signal;pid is $pid "); }else{ $signal = pcntl_wtermsig($status); FileLog::log("service stop by signal $signal;pid is $pid"); }}
上面的这个代码就通过根据pcntl_waitpid的返回结果和状态码对子进程因为不同原因中断做了不同的处理