PHP多进程系列笔记(二)
上一篇文章讲解了pcntl_fork和pcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识。
僵尸(zombie)进程
这里说下僵尸进程:
僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程。任何进程在退出前(使用exit退出) 都会变成僵尸进程(用于保存进程的状态等信息),然后由init进程接管。如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。
通过如下命令查看是否有僵尸进程,如果有,类似下面这样:
$ ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
Z+ 282 283 [php] <defunct>
或者使用top命令查看:
$ top
top - 08:26:38 up 57 min, 1 user, load average: 0.29, 0.37, 0.41
Tasks: 274 total, 1 running, 218 sleeping, 0 stopped, 1 zombie
也会显示 1 zombie 。
什么情况下会出现僵尸进程呢?
- 如果子进程还没有结束时,父进程就结束了,那么init进程会自动接手这个子进程,进行回收。
- 如果父进程是循环,又没有安装
SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束。那么子进程结束后,没有回收,就产生僵尸进程了。
示例:
fork_zombie.php
<?php
$pid = pcntl_fork();
if($pid == -1){
exit("fork fail");
}elseif($pid){
$id = getmypid();
echo "Parent process,pid {$id}, child pid {$pid}\n";
while(1){sleep(3);} //#1
}else{
$id = getmypid();
echo "Child process,pid {$id}\n";
sleep(2);
exit();
}
命令行里运行程序,然后新终端查看:
$ ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
Z+ 7252 7253 [php] <defunct>
出现了一个僵尸进程。这时候就算手动结束脚本程序也无法关闭这个僵尸子进程了。需要使用kill -9关闭。
pcntl_signal
bool pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] )
该函数为signo指定的信号安装一个新的信号处理器。
安装SIGCHLD信号
上一节里,我们讲到僵尸进程产生的原因:
如果父进程是循环,又没有安装
SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束。那么子进程结束后,没有回收,就产生僵尸进程了。
本小节我们通过安装SIGCHLD信号处理函数来解决僵尸进程问题。示例:
<?php
//表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器
declare(ticks = 1);
//安装SIGCHLD信号
pcntl_signal(SIGCHLD, function(){
echo "SIGCHLD \r\n";
pcntl_wait($status);
}); //#2
$pid = pcntl_fork();
if($pid == -1){
exit("fork fail");
}elseif($pid){
$id = getmypid();
echo "Parent process,pid {$id}, child pid {$pid}\n";
//先sleep一下,否则代码一直循环,无法处理信号接收
while(1){sleep(3);} //#1
}else{
$id = getmypid();
echo "Child process,pid {$id}\n";
sleep(2);
exit();
}
第一次注释掉#1和#2处的代码,父进程提前结束,子进程被init进程接手,所以没有产生僵尸进程。
第二次我们注释掉#2处的代码,开启#1处的代码,即父进程是个死循环,又没有回收子进程,就产生僵尸进程了。
第三次我们开启#1处和#2处的代码,父进程由于安装了信号处理,并调用wait函数等待子进程结束,所以也没有产生僵尸进程。
对子进程的结束不感兴趣
如果父进程不关心子进程什么时候结束,那么可以用pcntl_signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号。这样我们就不写子进程退出的处理函数了。
说明:
如果去掉
declare( ticks = 1 );无法响应信号。因php的信号处理函数是基于ticks来实现的,而不是注册到真正系统底层的信号处理函数中。
安装其他信号
我们可以在主进程安装更多信号,例如:
<?php
declare( ticks = 1 );
//信号处理函数
function sig_handler ( $signo )
{
switch ( $signo ) {
case SIGTERM :
// 处理SIGTERM信号
exit;
break;
case SIGHUP :
//处理SIGHUP信号
break;
case SIGUSR1 :
echo "Caught SIGUSR1...\n" ;
break;
default:
// 处理所有其他信号
}
}
echo "Installing signal handler...\n" ;
//安装信号处理器
pcntl_signal ( SIGTERM , "sig_handler" );
pcntl_signal ( SIGHUP , "sig_handler" );
pcntl_signal ( SIGUSR1 , "sig_handler" );
echo "Generating signal SIGTERM to self...\n" ;
//向当前进程发送SIGUSR1信号
posix_kill ( posix_getpid (), SIGUSR1 );
echo "Done\n"
注:通过
kill -l可以看到Linux下所有的信号常量。
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
防盗版声明:本文系原创文章,发布于公众号飞鸿影的博客(fhyblog)及博客园,转载需作者同意。
ticks相关
PHP的
ticks=1表示每执行1行PHP代码就回调此函数(指的pcntl_signal_dispatch)。实际上大部分时间都没有信号产生,但ticks的函数一直会执行。如果一个服务器程序1秒中接收1000次请求,平均每个请求要执行1000行PHP代码。那么PHP的pcntl_signal,就带来了额外的 1000 * 1000,也就是100万次空的函数调用。这样会浪费大量的CPU资源。
(摘自:韩天峰(Rango)的博客 » PHP官方的pcntl_signal性能极差
http://rango.swoole.com/archives/364)
pcntl_signal_dispatch的作用就是查看是否收到了信号需要处理,如果有信号的话,就调用相应的信号处理函数。
所以上述问题比较好的做法是去掉ticks,转而手动调用pcntl_signal_dispatch,在代码循环中自行处理信号。
我们把上一小节的例子改改,不使用ticks:
<?php
//declare( ticks = 1 );
//信号处理函数
function sig_handler ( $signo )
{
switch ( $signo ) {
case SIGUSR1 :
echo "Caught SIGUSR1...\n" ;
break;
default:
// 处理所有其他信号
}
}
echo "Installing signal handler...\n" ;
//安装信号处理器
pcntl_signal ( SIGUSR1 , "sig_handler" );
echo "Generating signal SIGTERM to self...\n" ;
//向当前进程发送SIGUSR1信号
posix_kill ( posix_getpid (), SIGUSR1 );
pcntl_signal_dispatch();
echo "Done\n";
运行结果:
Installing signal handler...
Generating signal SIGTERM to self...
Caught SIGUSR1...
Done
相比每执行一条php语句都会调用 pcntl_signal_dispatch 一次,效率好多了。
pcntl_alarm
int pcntl_alarm ( int $seconds )
该函数创建一个计时器,在指定的秒数后向进程发送一个 SIGALRM 信号。每次对 pcntl_alarm() 的调用都会取消之前设置的alarm信号。注意不是定时器,只会运行一次。
下面是一个隔5秒发送一个SIGALRM信号,并由signal_handler函数获取,然后打印一个 SIGALRM 的例子:
<?php
declare(ticks = 1);
//安装SIGALRM信号
pcntl_signal(SIGALRM, function(){
echo "SIGALRM\n";
pcntl_alarm(5); //再次调用,会重新发送一个SIGALRM信号
});
pcntl_alarm(5);//发送一个SIGALRM信号
echo "run...\n";
//死循环,否则进程会退出
while(1){sleep(1);}
注:如果不想使用ticks,那么需要在主循环里主动增加
pcntl_signal_dispatch()调用。
(未完待续)
PHP多进程系列笔记(二)的更多相关文章
- PHP多进程系列笔记(一)
本系列文章将向大家讲解pcntl_*系列函数,从而更深入的理解进程相关知识. PCNTL在PHP中进程控制支持默认是关闭的.您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或 ...
- PHP多进程系列笔记(五)
前面几节都是讲解pcntl扩展实现的多进程程序.本节给大家介绍swoole扩展的swoole_process模块. swoole多进程 swoole_process 是swoole提供的进程管理模块, ...
- PHP多进程系列笔记(三)
本节讲解几个多进程的实例. 多进程实例 Master-Worker结构 下面例子实现了简单的多进程管理: 支持设置最大子进程数 Master-Worker结构:Worker挂掉,Master进程会重新 ...
- PHP多进程系列笔记(四)
本节主要讲解Posix常用函数和进程池的概念,也会涉及到守护进程的知识.本节难度较低. Posix常用函数 posix_kill 向指定pid进程发送信号.成功时返回 TRUE , 或者在失败时返回 ...
- 跟着鸟哥学Linux系列笔记3-第11章BASH学习
跟着鸟哥学Linux系列笔记0-扫盲之概念 跟着鸟哥学Linux系列笔记0-如何解决问题 跟着鸟哥学Linux系列笔记1 跟着鸟哥学Linux系列笔记2-第10章VIM学习 认识与学习bash 1. ...
- Java系列笔记(1) - Java 类加载与初始化
目录 类加载器 动态加载 链接 初始化 示例 类加载器 在了解Java的机制之前,需要先了解类在JVM(Java虚拟机)中是如何加载的,这对后面理解java其它机制将有重要作用. 每个类编译后产生一个 ...
- Java系列笔记(6) - 并发(上)
目录 1,基本概念 2,volatile 3,atom 4,ThreadLocal 5,CountDownLatch和CyclicBarrier 6,信号量 7,Condition 8,Exchang ...
- webpy使用笔记(二) session/sessionid的使用
webpy使用笔记(二) session的使用 webpy使用系列之session的使用,虽然工作中使用的是django,但是自己并不喜欢那种大而全的东西~什么都给你准备好了,自己好像一个机器人一样赶 ...
- 《MFC游戏开发》笔记二 建立工程、调整窗口
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9300383 作者:七十一雾央 新浪微博:http:/ ...
随机推荐
- app.json
APP.JSON 文件来对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.设置多 TAB 等.相当于iOS开发中的AppDelegate 注意该文件不可添加任何注释. { ...
- 20169207《linux内核原理与分析》第二周作业
第一部分:学习MOOC网Linux内核分析的课程. 首先对冯诺依曼体系结构和存储程序计算机工作模型进行了了解,查阅资料,对冯诺依曼体系的特点与课堂上的相结合,真实明白了模型的特点. 在汇编C语言程序时 ...
- python迭代器实例
1. 迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退. 1.1 使用迭代 ...
- 6.关键字static
在java中并不存在全局变量的概念,但是我们可以通过static关键字来实现一个“为全局”的概念,在java中static表示“全局”和“静态”的意思,他可以用来修饰成员变量和方法,也可以用来修饰代码 ...
- SRM471
250pt: 题意:定义一种函数f(x),表示x不断/2直到出现非素数的操作次数.现在给定N,D.求X<= N, 并且f(x) >= D的最大的数 思路:直接先弄一个1000w以内的质数表 ...
- hdu 4455 Substrings(计数)
题目链接:hdu 4455 Substrings 题目大意:给出n,然后是n个数a[1] ~ a[n], 然后是q次询问,每次询问给出w, 将数列a[i]分成若干个连续且元素数量为w的集合,计算每个集 ...
- PHP内存溢出Allowed memory size of 解决办法
PHP内存溢出Allowed memory size of 解决办法 博客分类: php ============================Allowed memory size of x ...
- 深度学习主机环境配置: Ubuntu16.04+GeForce GTX 1080+TensorFlow
接上文<深度学习主机环境配置: Ubuntu16.04+Nvidia GTX 1080+CUDA8.0>,我们继续来安装 TensorFlow,使其支持GeForce GTX 1080显卡 ...
- Python——面向对象(初级篇)
1.如何创建类 class 类名: pass 2.如何创建方法 构造方法: __init__ obj = 类名('a1') 普通方法: obj = 类名('xxx') obj.普通方法名() 3.图解 ...
- unity 人工智能AI,装备解锁临时笔记
A*算法的一种改进设想:1.如何让角色到达目标点的过程中更加平滑:获取一串到达目标点的网格串之后,就实时用带形状的物理射线检测能否直接到达下一个目标点的再下一个目标点,如果能到达,那么直接朝该方向运动 ...