上一节中介绍了信号的基本处理方式。在本节中,会结合实际的例子来具体的说明。
本节中的示例来自用PHP实现的一个多进程管理工具,项目地址:https://github.com/bruceding/PHP-Thread。此工具基于pcntl,通过一个主进程(master)管理多个子进程,并且子进程的数量是可以动态变化的。
当主进程fork子进程的时候,我们需要对信号进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/** * _registerSigHandler * 信号注册 * * @access private * @return void */ protected function _registerSigHandler() { pcntl_signal(SIGTERM, array($this, '_sigHandler')); pcntl_signal(SIGHUP, array($this, '_sigHandler')); pcntl_signal(SIGCHLD, array($this, '_sigHandler')); pcntl_signal(SIGINT, array($this, '_sigHandler')); pcntl_signal(SIGQUIT, array($this, '_sigHandler')); } /** * _sigHandler * 信号处理 * * @param mixed $sig * @access private * @return void */ protected function _sigHandler($sig) { switch(intval($sig)) { case SIGCHLD: $this->_waitChild(); break; case SIGINT: case SIGQUIT: case SIGHUP: case SIGTERM: $this->_cleanup(); break; default: break; } } |
上面代码共处理了五种信号。当子进程执行完成调用exit时,会产生SIGCHLD信号,这时,主进程应该捕获此信号进行处理。调用pcntl_waitpid等函数。否则,子进程会变成僵尸进程,子进程的结构还会存在于内存中,造成资源浪费。
对于SIGINT,SIGQUIT,SIGHUP,SIGTERM信号,主进程进行了清理操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * _cleanup * 父进程接受到退出信号时,给子进程发送SIGTERM信号 * * @access protected * @return void */ protected function _cleanup() { if (!$this->isParent) { return false; } if (count($this->_child)) { foreach ($this->_child as $pid => $thread) { posix_kill($pid, SIGTERM); } } } |
对于子进程传递SIGTERM信号,让其平滑关闭。
在子进程中也进行了信号处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
/** * _registerSigHandler * 注册子进程信息号 * * @access protected * @return void */ protected function _registerSigHandler() { // 退出信号忽略 pcntl_signal(SIGINT, SIG_IGN); pcntl_signal(SIGHUP, SIG_IGN); pcntl_signal(SIGQUIT, SIG_IGN); pcntl_signal(SIGTERM, array($this, '_sigHandler')); } /** * _sigHandler * 子进程信号处理 * * @param mixed $sig * @access protected * @return void */ protected function _sigHandler($sig) { switch(intval($sig)) { case SIGTERM: $this->stop(); break; default: break; } } |
对于SIGINT,SIGHUP,SIGQUIT信号子进程选择了忽略。对于SIGTERM信号,子进程调用stop函数。