上一篇文章讲解了 和 两个函数的使用,本篇继续讲解PHP多进程相关新知识。
僵尸(zombie)进程
这里说下僵尸进程:
僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程。任何进程在退出前(使用exit退出) 都会变成僵尸进程(用于保存进程的状态等信息),然后由init进程接管。如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。
通过如下命令查看是否有僵尸进程,如果有,类似下面这样:
如果子进程还没有结束时,父进程就结束了,那么init进程会自动接手这个子进程,进行回收。
如果父进程是循环,又没有安装 信号处理函数调用 或 等待子进程结束。那么子进程结束后,没有回收,就产生僵尸进程了。
示例: fork_zombie.php
命令行里运行程序,然后新终端查看:
出现了一个僵尸进程。这时候就算手动结束脚本程序也无法关闭这个僵尸子进程了。需要使用 关闭。
pcntl_signal
该函数为 指定的信号安装一个新的信号处理器。
安装SIGCHLD信号
上一节里,我们讲到僵尸进程产生的原因:
如果父进程是循环,又没有安装 信号处理函数调用 或 等待子进程结束。那么子进程结束后,没有回收,就产生僵尸进程了。
本小节我们通过安装SIGCHLD信号处理函数来解决僵尸进程问题。示例:
第一次注释掉 和 处的代码,父进程提前结束,子进程被init进程接手,所以没有产生僵尸进程。 第二次我们注释掉 处的代码,开启 处的代码,即父进程是个死循环,又没有回收子进程,就产生僵尸进程了。 第三次我们开启 处和 处的代码,父进程由于安装了信号处理,并调用wait函数等待子进程结束,所以也没有产生僵尸进程。
对子进程的结束不感兴趣如果父进程不关心子进程什么时候结束,那么可以用 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号。这样我们就不写子进程退出的处理函数了。
说明:
如果去掉 无法响应信号。因php的信号处理函数是基于ticks来实现的,而不是注册到真正系统底层的信号处理函数中。
安装其他信号
我们可以在主进程安装更多信号,例如:
注:通过 可以看到Linux下所有的信号常量。
防盗版声明:本文系原创文章,发布于公众号 及博客园,转载需作者同意。
ticks相关
PHP的 表示每执行1行PHP代码就回调此函数(指的 )。实际上大部分时间都没有信号产生,但ticks的函数一直会执行。如果一个服务器程序1秒中接收1000次请求,平均每个请求要执行1000行PHP代码。那么PHP的pcntlsignal,就带来了额外的 1000 * 1000,也就是100万次空的函数调用。这样会浪费大量的CPU资源。 (摘自:韩天峰(Rango)的博客 » PHP官方的pcntlsignal性能极差 http://rango.swoole.com/archives/364)
pcntlsignaldispatch的作用就是查看是否收到了信号需要处理,如果有信号的话,就调用相应的信号处理函数。
所以上述问题比较好的做法是去掉ticks,转而手动调用 ,在代码循环中自行处理信号。
我们把上一小节的例子改改,不使用ticks:
运行结果:
相比每执行一条php语句都会调用 一次,效率好多了。
pcntl_alarm
该函数创建一个计时器,在指定的秒数后向进程发送一个 信号。每次对 的调用都会取消之前设置的alarm信号。注意不是定时器,只会运行一次。
下面是一个隔5秒发送一个SIGALRM信号,并由signal_handler函数获取,然后打印一个 的例子:
注:如果不想使用ticks,那么需要在主循环里主动增加 调用。
(未完待续)
领取专属 10元无门槛券
私享最新 技术干货