[原文地址:https://blog.ti-node.com/blog...]php
上一篇尬聊了通篇的pcntl_wait()和pcntl_waitpid(),就是为了解决僵尸进程的问题,但最后看起来仍是有一些遗留问题,并且由于嘴欠在上篇文章的结尾出也给了解决方案:信号。node
信号是一种软件中断,也是一种很是典型的异步事件处理方式。在NIX系统诞生的混沌之初,信号的定义是比较混乱的,并且最关键是不可靠,这是一个很严重的问题。因此在后来的POSIX标准中,对信号作了标准化同时也各个发行版的NIX也都提供大量可靠的信号。每种信号都有本身的名字,大概如SIGTERM、SIGHUP、SIGCHLD等等,在*NIX中,这些信号本质上都是整形数字(游有心情的能够参观一下signal.h系列头文件)。服务器
信号的产生是有多种方式的,下面是常见的几种:ssh
而进程在收到信号后,能够有以下三种响应:异步
用人话来表达,就是说假如你是一个进程,你正在干活,忽然施工队的喇叭里冲你嚷了一句:“吃饭了!”,因而你就放下手里的活儿去吃饭。你正在干活,忽然施工队的喇叭里冲你嚷了一句:“发工资了!”,因而你就放下手里的活儿去领工资。你正在干活,忽然施工队的喇叭里冲你嚷了一句:“有人找你!”,因而你就放下手里的活儿去看看是谁找你什么事情。固然了,你很任性,那是彻底能够不鸟喇叭里喊什么内容,也就是忽略信号。也能够更任性,当喇叭里冲你嚷“吃饭”的时候,你去就不去吃饭,你去睡觉,这些均可以由你来。而你在干活过程当中,历来不会由于要等某个信号就不干活了一直等信号,而是信号随时随地均可能会来,而你只须要在这个时候做出相应的回应便可,因此说,信号是一种软件中断,也是一种异步的处理事件的方式。函数
回到上文所说的问题,就是子进程在结束前,父进程就已经先调用了pcntl_waitpid(),致使子进程在结束后依然变成了僵尸进程。实际上在父进程不断while循环调用pcntl_waitpid()是个解决办法,大概代码以下:spa
$pid = pcntl_fork(); if( 0 > $pid ){ exit('fork error.'.PHP_EOL); } else if( 0 < $pid ) { // 在父进程中 cli_set_process_title('php father process'); // 父进程不断while循环,去反复执行pcntl_waitpid(),从而试图解决已经退出的子进程 while( true ){ sleep( 1 ); pcntl_waitpid( $pid, &$status, WNOHANG ); } } else if( 0 == $pid ) { // 在子进程中 // 子进程休眠3秒钟后直接退出 cli_set_process_title('php child process'); sleep( 20 ); exit; }
下图是运行结果:rest
可是这样的代码有一个缺陷,实际上就是子进程已经退出的状况下,主进程还在不断while pcntl_waitpid()去回收子进程,这是一件很奇怪的事情,并不符合社会主义主流价值观,不低碳不节能,代码也不优雅,很差看。因此,应该考虑用更好的方式来实现。那么,咱们篇头提了许久的信号终于概要出场了。code
如今让咱们考虑一下,为什么信号能够解决“不低碳不节能,代码也不优雅,很差看”的问题。子进程在退出的时候,会向父进程发送一个信号,叫作SIGCHLD,那么父进程一旦收到了这个信号,就能够做出相应的回收动做,也就是执行pcntl_waitpid(),从而解决掉僵尸进程,并且还显得咱们代码优雅好看节能环保。blog
梳理一下流程,子进程向父进程发送SIGCHLD信号是对人们来讲是透明的,也就是说咱们无须关心。可是,咱们须要给父进程安装一个响应SIGCHLD信号的处理器,除此以外,还须要让这些信号处理器运行起来,安装上了不运行是一件尴尬的事情。那么,在php里给进程安装信号处理器使用的函数是pcntl_signal(),让信号处理器跑起来的函数是pcntl_signal_dispatch()。
下面结合新引入的两个函数来解决一下楼上的丑陋代码:
$pid = pcntl_fork(); if( 0 > $pid ){ exit('fork error.'.PHP_EOL); } else if( 0 < $pid ) { // 在父进程中 // 给父进程安装一个SIGCHLD信号处理器 pcntl_signal( SIGCHLD, function() use( $pid ) { echo "收到子进程退出".PHP_EOL; pcntl_waitpid( $pid, $status, WNOHANG ); } ); cli_set_process_title('php father process'); // 父进程不断while循环,去反复执行pcntl_waitpid(),从而试图解决已经退出的子进程 while( true ){ sleep( 1 ); // 注释掉原来老掉牙的代码,转而使用pcntl_signal_dispatch() //pcntl_waitpid( $pid, &$status, WNOHANG ); pcntl_signal_dispatch(); } } else if( 0 == $pid ) { // 在子进程中 // 子进程休眠3秒钟后直接退出 cli_set_process_title('php child process'); sleep( 20 ); exit; }
运行结果以下: