PHP 能够经过pcntl
扩展实现多进程编程, 而网上关于如何经过pcntl
建立多进程的在这里就不表了, 我主要说说关于pcntl_fork
的一个坑和相关的比较生僻的几个函数的使用方式, 这也是经过挖坑和填坑得出的结论。
闲言碎语不要讲, 直接开始php
在实践中, 我在使用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_39 fork_child_process_1_62656_62659_51 fork_child_process_1_62658_62660_22 fork_child_process_2_62656_62661_91 fork_child_process_2_62658_62662_22 fork_child_process_2_62659_62663_82 fork_child_process_2_62660_62664_59 fork_process_pid_62225_62656_48 fork_process_pid_62656_62658_22 fork_process_pid_62656_62659_82 fork_process_pid_62656_62661_65 fork_process_pid_62658_62660_59 fork_process_pid_62658_62662_59 fork_process_pid_62659_62663_61 fork_process_pid_62660_62664_10
为什么会出现上面的结果, 这是由于在fork以后, 原有的进程会分裂为两个进程, 一个主进程, 一个子进程, fork后面全部的代码都是共享的, 虽然经过fork的返回值能够判断是主进程仍是子进程来执行相应的子进程或主进程逻辑,但以后子进程本身又走到了for循环的部分, 子进程本身有建立了子进程, 因此上面看到了多个child_process 文件, 至于为何是7个,
来分析一下。code
=====================华丽的分割线=============================继承
循环变量$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
这里能够看到62656
就是主进程a的pid.进程
至此, 主进程a的循环完毕, 在看看a
建立的第一个子进程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
的pid 62658
,
同理aaa
,aab
也继承了aa
的$i值,这时$i的值为1, 当继续循环时, $i 就变成了2, 也就只能循环一次了,相应aaa
,aab
建立了子进程文件fork_child_process_2_62659_62663_82
(aaaa),fork_child_process_2_62660_62664_59
(aaba),而他们相应的父进程就是aaa
(62659)和aab
(62660).get
至此, for循环中的多进程逻辑完成了, 也就是为什么产生了第一部分的7个文件it
=====================华丽的分割线=============================io
而至于为什么第二部分是8个文件, 各位能够本身思考一下, 注意, 不管主进程仍是子进程, 在for循环完毕以后会继续往下走, 知道这一点就好理解了。for循环
在实际的代码中, 我就犯了这种错误。
那如何解决上面的问题呢, 只要在子进程执行的最后exit
就好啦,
fork_child_process_0_63219_63221_66 fork_child_process_1_63219_63222_88 fork_child_process_2_63219_63223_22 fork_process_pid_62225_63219_77
继续,那么在网上看到不少多进程编程中使用pcntl_waitpid
, 并不了解他是作什么的,且相应的例子不多, 我暂且来讲说个人理解
等待或返回fork的子进程状态。
多进程的主进程建立了子进程,那主进程如何确认子进程的状态呢。 假如主进程须要根据子进程的状态作不一样的处理呢, 这里的状态包括子进程被kill掉,或变成僵尸进程等。 pcntl_waitpid就能够获取子进程的状态码, 经过这个状态码, 就可知道子进程处于什么状态
他的用法:
int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )
返回的值能够是-1,0或者 >0的值, 若是是-1, 表示子进程出错, 若是>0表示子进程已经退出且值是退出的子进程pid,至于如何退出, 能够经过$status状态码反应。 那何时返回0呢, 只有在option
参数为 WNOHANG
且子进程正在运行时0, 也就是说当设置了options=WNOHANG
时, 若是子进程尚未退出, 此时pcntl_waitpid
就会返回0
另外, 若是不设置这个参数为WNOHANG
, pcntl_waitpid
就会阻塞运行
, 直到子进程退出, 至于option
的另一个值WUNTRACED
, 暂未理解, 不表
那么如何根据$status(状态码)判断进程是如何退出呢, 以下(参数都是$status)
这个函数能够根据$status 判断进程是否正常退出, 何为正常退出, 好比exit
这个函数仅在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
的返回结果和状态码对子进程由于不一样缘由中断作了不一样的处理