pcntl_signal 失效主因是 Composer 自动加载器干扰信号机制;PHP 信号需 pcntl_async_signals(true)(PHP≥7.1)或手动调用 pcntl_signal_dispatch() 才能触发,而 Composer 默认轮询不保证及时分发。

为什么 pcntl_signal 在 Composer 环境下常失效
直接调用 pcntl_signal 但信号没触发?大概率是 PHP 的信号处理机制被 Composer 自动加载器干扰了。Composer 默认启用 pcntl_signal_dispatch() 的轮询式分发(尤其在 autoload.php 加载后),而 PHP 的 POSIX 信号默认是“惰性分发”——除非显式调用 pcntl_signal_dispatch() 或启用了 pcntl_async_signals(true),否则信号回调根本不会执行。
PHP-Signals 库本质是封装了这些底层细节,但它不自动开启异步信号支持,也不强制你插入调度点。所以即使 require 'vendor/autoload.php' 成功,信号仍可能静默丢失。
-
pcntl_async_signals(true)必须在任何信号注册前调用,且仅 PHP ≥ 7.1 支持 - 若使用 PHP pcntl_signal_dispatch()
- Composer 的 autoloader 本身不阻塞信号,但它的初始化时机可能掩盖信号注册顺序问题
安装 PHP-Signals 并确认扩展依赖
PHP-Signals 是纯 PHP 实现的信号包装器,但它依赖底层 pcntl 扩展——这个扩展默认不启用,且不能通过 extension=pcntl 在 Web SAPI(如 Apache、FPM)中启用,只适用于 CLI 模式。
- 运行
php -m | grep pcntl,无输出说明未启用;需重新编译 PHP 或修改php.ini(CLI 配置文件,非 Web 配置) - 执行
composer require wyrihaximus/php-signals,它不提供pcntl,只是帮你更安全地注册回调 - 注意:该库 v2+ 要求 PHP ≥ 8.0;若用 PHP 7.4,需锁定
"wyrihaximus/php-signals": "^1.0"
正确注册信号处理器的三步顺序
顺序错一点,SIGINT 就收不到。PHP-Signals 不改变底层规则,只是让写法更清晰。
立即学习“PHP免费学习笔记(深入)”;
<?php
// 1. 必须最先启用异步信号(PHP ≥ 7.1)
pcntl_async_signals(true);
// 2. 然后引入 autoload 和 Signals 类
require __DIR__ . '/vendor/autoload.php';
use WyriHaximus\Signals\Signals;
// 3. 注册信号 —— 此时才真正绑定到内核
Signals::on(SIGINT, function (int $signal) {
echo "Caught SIGINT, exiting cleanly...\n";
exit(0);
});
// 主逻辑(例如长循环)
while (true) {
sleep(1);
}
- 把
pcntl_async_signals(true)放在require autoload.php之前,避免某些 autoloader 初始化意外触发信号分发 - 不要在闭包里做耗时操作(如 file_put_contents、数据库写入),信号处理器应轻量,否则可能被下一个信号中断
-
Signals::on()内部仍调用pcntl_signal(),所以它不解决pcntl本身的限制(比如无法捕获SIGKILL、SIGSTOP)
调试信号是否真正生效的实操方法
别靠 kill -INT $pid 猜,用 strace 直接看系统调用。
- 启动脚本时加
strace -e trace=rt_sigaction,rt_sigprocmask,kill php script.php,观察是否有rt_sigaction调用成功 - 若看到
rt_sigaction(SIGINT, {...}, NULL, 8) = 0,说明注册成功;若只有rt_sigprocmask没有rt_sigaction,说明Signals::on()根本没执行或被跳过 - 在信号处理器里加
error_log("SIGUSR1 handled", 4),查stderr或系统日志,比echo更可靠(因为echo可能被缓冲或重定向丢弃)
信号处理不是“设完就跑”,它高度依赖 PHP 版本、SAPI 模式、扩展状态和代码执行顺序。最容易被忽略的是:CLI 的 php.ini 和 Web 的 php.ini 是两套配置,pcntl 必须单独为 CLI 启用,且 pcntl_async_signals() 的调用位置比类引入还关键。



















