php用pcntl_signal捕获sigint需先declare(ticks=1),再pcntl_signal(sigint, $handler),并在循环中调用pcntl_signal_dispatch()分发;不可在回调中执行非异步信号安全操作,应设标志位由主循环处理退出。

PHP怎么用 pcntl_signal 捕获 SIGINT
PHP 本身不自动响应系统信号,必须显式注册处理函数,pcntl_signal 是唯一入口。它不阻塞信号,但默认行为是“忽略”或“终止进程”,比如按 Ctrl+C 发送 SIGINT,不注册就会直接退出。
常见错误是调用后没配合 pcntl_signal_dispatch 或没启用 ticks —— PHP 不像 C 那样实时触发,得靠 ticks 主动轮询检查是否有待处理信号。
- 必须在脚本开头加
declare(ticks = 1),否则信号永远不会被分发 -
pcntl_signal(SIGINT, function($sig) { echo "收到中断\n"; })注册后,仅表示“记下了”,真正执行要靠后续的pcntl_signal_dispatch() - 长期运行的 CLI 脚本(如守护进程)通常放在
while (true)循环里,每次循环末尾调一次pcntl_signal_dispatch()
为什么 pcntl_signal 不支持异步信号安全函数
PHP 的信号处理不是在中断上下文中执行,而是在用户态 tick 边界“模拟”触发,所以不能调用 echo、file_put_contents、exit 等非异步信号安全函数 —— 它们可能重入或破坏内部状态,导致崩溃或静默失败。
典型表现:信号处理函数里写日志,程序突然 segfault;或者看似执行了,但实际没输出、没写入文件。
立即学习“PHP免费学习笔记(深入)”;
动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包
- 安全操作只有极少数:
pcntl_signal自身、pcntl_signal_dispatch、pcntl_alarm、简单变量赋值 - 想记录日志?改用
syslog()(POSIX 兼容系统)或提前打开 fd 写入(需stream_set_blocking(STDERR, false)避免阻塞) - 想退出?设个全局标志位(如
$should_exit = true),主循环检查它,而不是在信号回调里调exit()
pcntl_signal 和 pcntl_async_signals 的区别
pcntl_async_signals(true) 是 PHP 7.1+ 引入的替代方案,它绕过 ticks 机制,让信号真正异步到达,但代价是兼容性更差、调试更难。
你用它,就别再 declare ticks;但它在某些 SAPI(如 Apache mod_php)下完全不可用,且部分信号(如 SIGCHLD)仍需手动 waitpid,容易漏收子进程退出事件。
- CLI 场景下推荐
pcntl_async_signals(true)+pcntl_signal(SIGCHLD, ...)组合,代码更干净 - 但若需兼容 PHP 7.0 或跑在容器里不确定 SAPI 类型,老式
declare(ticks=1)更稳 - 注意:
pcntl_async_signals开启后,pcntl_signal_dispatch不再生效,也不能混用两种模式
SIGTERM 和 SIGUSR1 在守护进程里的典型用法
Linux 守护进程常用 SIGTERM 请求优雅退出,SIGUSR1 触发日志轮转或配置重载 —— 这些不是 PHP 默认行为,全靠你自己定义。
容易踩的坑是没做信号屏蔽(signal mask),导致多个相同信号连续到来时被合并(如快速连发两次 SIGUSR1,只触发一次回调),或者子进程继承了父进程的信号处理逻辑,干扰主流程。
- 用
pcntl_sigprocmask(SIG_BLOCK, [SIGUSR1])在关键区段临时屏蔽,处理完再恢复 - 子进程启动前,用
pcntl_signal(SIGUSR1, SIG_DFL)重置为默认行为,避免误响应 - 测试时别只用
kill -USR1 $(pidof php),要确认进程确实收到了:加一句pcntl_signal_get_handler(SIGUSR1)查当前 handler 是否是你设的函数
SIGTERM 来了,你得确保 fclose 前数据已 flush,且不能让其他线程/协程同时操作同一资源。这些细节没法靠 signal 函数自动解决,得靠你自己设计好退出路径。










