资讯专栏INFORMATION COLUMN

PHP多进程系列笔记(二)

CatalpaFlat / 2931人阅读

摘要:任何进程在退出前使用退出都会变成僵尸进程用于保存进程的状态等信息,然后由进程接管。这时候就算手动结束脚本程序也无法关闭这个僵尸子进程了。那么子进程结束后,没有回收,就产生僵尸进程了。本小节我们通过安装信号处理函数来解决僵尸进程问题。

上一篇文章讲解了pcntl_forkpcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识。

僵尸(zombie)进程

这里说下僵尸进程:

僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程。任何进程在退出前(使用exit退出) 都会变成僵尸进程(用于保存进程的状态等信息),然后由init进程接管。如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。

通过如下命令查看是否有僵尸进程,如果有,类似下面这样:

$ ps -A -o stat,ppid,pid,cmd | grep -e "^[Zz]"
Z+     282   283 [php] 

如果子进程还没有结束时,父进程就结束了,那么init进程会自动接手这个子进程,进行回收。

如果父进程是循环,又没有安装SIGCHLD信号处理函数调用waitwaitpid()等待子进程结束。那么子进程结束后,没有回收,就产生僵尸进程了。

示例:
fork_zombie.php


命令行里运行程序,然后新终端查看:

$ ps -A -o stat,ppid,pid,cmd | grep -e "^[Zz]"
Z+    7252  7253 [php] 

出现了一个僵尸进程。这时候就算手动结束脚本程序也无法关闭这个僵尸子进程了。需要使用kill -9关闭。

pcntl_signal
bool pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] )

该函数为signo指定的信号安装一个新的信号处理器。

安装SIGCHLD信号

上一节里,我们讲到僵尸进程产生的原因:

如果父进程是循环,又没有安装SIGCHLD信号处理函数调用waitwaitpid()等待子进程结束。那么子进程结束后,没有回收,就产生僵尸进程了。

本小节我们通过安装SIGCHLD信号处理函数来解决僵尸进程问题。示例:


第一次注释掉#1#2处的代码,父进程提前结束,子进程被init进程接手,所以没有产生僵尸进程。
第二次我们注释掉#2处的代码,开启#1处的代码,即父进程是个死循环,又没有回收子进程,就产生僵尸进程了。
第三次我们开启#1处和#2处的代码,父进程由于安装了信号处理,并调用wait函数等待子进程结束,所以也没有产生僵尸进程。

对子进程的结束不感兴趣
如果父进程不关心子进程什么时候结束,那么可以用pcntl_signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号。这样我们就不写子进程退出的处理函数了。

说明:

如果去掉declare( ticks  =  1 );无法响应信号。因php的信号处理函数是基于ticks来实现的,而不是注册到真正系统底层的信号处理函数中。
安装其他信号

我们可以在主进程安装更多信号,例如:


注:通过 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/archi...

pcntl_signal_dispatch的作用就是查看是否收到了信号需要处理,如果有信号的话,就调用相应的信号处理函数。

所以上述问题比较好的做法是去掉ticks,转而手动调用pcntl_signal_dispatch,在代码循环中自行处理信号。

我们把上一小节的例子改改,不使用ticks:


运行结果:

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 的例子:


注:如果不想使用ticks,那么需要在主循环里主动增加pcntl_signal_dispatch()调用。

(未完待续)


欢迎关注公众号及时获取最新文章推送!


推荐!每月仅需$2.5,即可拥有配置SSD的VPS!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28866.html

相关文章

  • PHP进程系列笔记(一)

    摘要:用于创建子进程。该函数阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。注意处需要注意子进程需要防止子进程也进入循环。如果没有,最终创建的子进程不只个。 本系列文章将向大家讲解pcntl_*系列函数,从而更深入的理解进程相关知识。 PCNTL在PHP中进程控制支持默认是关闭的。您需要使用 --enable-pcntl 配置选项重新编译PHP的 CGI或CLI版本...

    ddongjian0000 评论0 收藏0
  • PHP进程系列笔记(五)

    摘要:消息队列更常见的用途是主进程分配任务,子进程消费执行。子进程前面加了个,这是为了防止父进程还未往消息队列中加入内容直接退出。 前面几节都是讲解pcntl扩展实现的多进程程序。本节给大家介绍swoole扩展的swoole_process模块。 swoole多进程 swoole_process 是swoole提供的进程管理模块,用来替代PHP的pcntl扩展。 首先,确保安装的swoole...

    qianfeng 评论0 收藏0
  • PHP进程系列笔记(四)

    摘要:本节主要讲解常用函数和进程池的概念,也会涉及到守护进程的知识。所以任何时候,建议预先创建好进程,也就是使用进程池的方式实现。 本节主要讲解Posix常用函数和进程池的概念,也会涉及到守护进程的知识。本节难度较低。 Posix常用函数 posix_kill 向指定pid进程发送信号。成功时返回 TRUE , 或者在失败时返回 FALSE 。 bool posix_kill ( int $...

    Cc_2011 评论0 收藏0
  • PHP 进程系列笔记(三)

    摘要:本节讲解几个多进程的实例。新开终端,我们使用命令查看进程可以看到个进程个主进程,个子进程。使用命令结束子进程,主进程会重新拉起一个新的子进程。 本节讲解几个多进程的实例。 多进程实例 Master-Worker结构 下面例子实现了简单的多进程管理: 支持设置最大子进程数 Master-Worker结构:Worker挂掉,Master进程会重新创建一个

    focusj 评论0 收藏0
  • php进程插入数据(pcntl 学习笔记)

    摘要:进程切换太多,影响了了效率应该是原因之一。当时,十万条记录,个进程插入总时间为单进程插入万条数据,耗时秒,相对个进程插入万记录来说,耗时少些。而单进程插入万条记录,耗时,相对来说,是挺慢的了。 个人在虚拟机centos7,单核,1G内存 /** * 模拟并发请求,10万次写入数据库 * 拆分为10个进程,每个进程处理一万条插入 */ $total = 10000; $num ...

    CoyPan 评论0 收藏0

发表评论

0条评论

CatalpaFlat

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<