乐趣区

写给PHP程序员的信号处理教程

今天晚上吃卤煮,领桌的妹子问我,这玩意儿能吃么?我:你觉得能吃就能吃。。。和内容无关的主题
什么是信号
信号就是事件发生时,对进程的一种通知机制(也叫软件中断)。当一个进程收到信号后,内核会暂停该进程正在执行的代码,并跳转到对应的信号处理函数中,如果处理函数不中断,执行完处理函数后,会继续执行之前中断的地方往下执行。
我们在 FPM 模式下写代码,不会遇到信号处理相关的问题,但是 CLI 模式下一些常驻内存的脚本,如何能够自由的重启、关闭、退出前做一些清理工作(断开链接,删除临时文件等)?
C 的信号处理举例

上图中,我为信号 SIGINT 注册了处理函数 sigint_handle,捕获到信号后,输出内容后退出,简单易懂吧。执行 gcc -o run run.c && ./run,然后 CTRL+C(会触发 SIGINT 信号),成功输出:成功捕获到信号 2!,程序直接结束运行。
PHP 的信号处理举例

pcntl_signal 是 PHP 的信号处理注册方法,上面实现的功能和 C 实现的基本一致,不同的是,当前进程不会退出,并且多输出了一个 signinfo(PHP 是 C 写的,为啥刚刚 C 语言的没有信号相关的信息呢?因为 PHP 使用的是另一个信号函数 sigaction,有兴趣的可以了解一下)
PHP 的信号处理并不是直接调用 C

这个是 pcntl 初始化的时候,将 pcntl_signal_dispatch 注册为 tick 的处理函数
pcntl_signal 会将处理函数放到信号集合中(PHP 的 hash table),而 php_signale4 最终会调用 sigaction 进行底层的信号管理。
这里我省略了大量代码,将关键的点标记了出来,其实 PHP 维护一个自己的信号集合,每当调用 pcntl_signal_dispatch 时就会查询是否有信号,上面的 SIG_BLOCK 会将信号阻塞,这样只有我们把关键的代码执行完毕之后,再去触发信号处理函数以保证数据和程序逻辑的完整性。
PHP 如何优雅的处理信号
经常见到身边的程序员们,每当需要重启 PHP-FPM 进程的时候,使用的招数是 kill 掉所有 PHP 进程,然后新启动。一般情况没啥问题,但有些时候可能某个进程的任务还没执行完,直接把人家中断了略显粗暴。其实只要你给 PHP 的 Master 进程发送一条 USR2 信号,它便会再处理完所有任务后,重启子进程,这才是所谓的优雅~

上图是我简单写的一个例子,如果我们想让进程优雅退出的时候,只需要发送 SIGTERM 信号即可。需要注意的是 SIGKILL 和 SIGSTOP 信号会略过信号阻塞会将进程直接停止,还有就是信号会中断睡眠(SLEEP),sleep 如果没执行完会返回剩下的秒数,有兴趣可以试试。
信号相关的知识点其实有很多,还需要继续深入研究~ 上文中的 PHP 源码为 7.1.25 版本,各个版本可能不太一样。

退出移动版