PHP 8.1+ 首次原生支持 pcntl_async_signals(),仅限 CLI 模式,需 --enable-pcntl 编译,且必须配合 pcntl_signal() 和定期 pcntl_signal_dispatch() 使用,信号回调中仅允许异步信号安全操作。

PHP 8.1+ 才真正支持 pcntl_async_signals()
PHP 原生异步信号处理不是“开启开关”就能用的功能,它依赖版本、SAPI 和编译配置。低于 PHP 8.1 的版本(包括 8.0)调用 pcntl_async_signals(true) 会直接报错:Call to undefined function pcntl_async_signals()。PHP 8.1 是首个将异步信号支持从扩展行为转为内建能力的版本,且默认启用 —— 但前提是你的 PHP 是用 --enable-pcntl 编译的(绝大多数 Linux 发行版包默认包含),且**不能运行在 Web SAPI 下(如 Apache mod_php 或 FPM)**。
只能在 CLI 模式下使用 pcntl_async_signals()
Web SAPI(尤其是 FPM)自身已接管信号处理逻辑(如 SIGTERM 用于平滑重启),强行注册用户级信号处理器会导致不可预测行为,PHP 会直接拒绝启用:pcntl_async_signals(): async signals can only be used in CLI mode。即使你用 php -d extension=pcntl.so script.php 强制加载,只要进程不是 CLI 启动,该函数仍会失败。
- ✅ 正确启动方式:
php /path/to/your/script.php
- ❌ 错误方式:
curl http://localhost/script.php
(走 FPM)、php-fpm --force-stderr
(子进程仍是 FPM 管理) - ⚠️ 注意:
php -S内置服务器属于 CLI SAPI,可以使用,但仅限开发调试
pcntl_async_signals() 开启后仍需手动注册信号回调
启用异步信号只是让 PHP 在任意执行点(包括 opcode 中间)响应信号,不等于自动处理。你必须显式调用 pcntl_signal() 绑定回调,否则信号到达时仍按默认行为(如终止进程)处理。
pcntl_async_signals(true);
pcntl_signal(SIGUSR1, function (int $signo) {
echo "Received SIGUSR1\n";
});
// 必须定期调用 pcntl_signal_dispatch() 触发回调
while (true) {
pcntl_signal_dispatch(); // 关键:不调用则回调永不执行
usleep(10000);
}
常见错误是忘记 pcntl_signal_dispatch() —— 它不是后台线程,PHP 不会自动轮询。在长时间阻塞操作(如 fread(STDIN)、sleep())前,也建议插入一次 pcntl_signal_dispatch(),否则信号可能被延迟数秒才响应。
立即学习“PHP免费学习笔记(深入)”;
信号安全限制:回调里只能调用异步信号安全函数
POSIX 对信号处理函数有严格限制:只能调用可重入(async-signal-safe)函数,否则可能破坏内存或导致死锁。PHP 用户代码中,这意味着:
- ❌ 禁止调用:
echo、print_r、error_log、malloc相关(即所有动态内存分配)、任何扩展函数(如mysqli_query、file_get_contents) - ✅ 允许调用:
write()(注意:PHP 的fwrite(STDOUT, ...)底层是write,但需确保STDOUT未被重定向为缓冲流)、pcntl_signal()、exit()(仅限简单退出) - ? 实用做法:信号回调只设标志位,主循环检查并安全处理
例如:$should_reload = false; pcntl_signal(SIGUSR2, function () use (&$should_reload) { $should_reload = true; // 简单赋值是安全的 }); while (true) { pcntl_signal_dispatch(); if ($should_reload) { reload_config(); // 这里才调用完整业务逻辑 $should_reload = false; } usleep(50000); }
忽略信号安全规则是生产环境最隐蔽的崩溃来源之一,尤其在高并发长期运行的守护进程中 —— 表现可能是随机 core dump 或进程卡死,且极难复现。











