本文目录导读:

- 安装信号处理器(核心函数)
- 使用
pcntl_signal_dispatch()检查信号 - 使用
pcntl_async_signals()(PHP 7.1+) - 常见终止信号说明
- 实际应用场景
- 必须在 CLI 下运行
- 完整示例:带资源清理的守护进程
在 PHP 中,终止信号通常指的是 进程终止信号(Process Termination Signals),SIGTERM 或 SIGINT(Ctrl+C),PHP 本身并没有一个叫“终止信号”的内置函数,但你可以通过信号处理 来捕获和处理这些信号,实现优雅退出、资源清理等操作。
以下是 PHP 中与终止信号相关的核心方法和实现:
安装信号处理器(核心函数)
pcntl_signal()
安装一个信号处理器,当进程收到指定信号时,会调用自定义的回调函数。
<?php
declare(ticks = 1); // 低版本 PHP 需要此声明,PHP 7.1+ 不建议用 ticks,推荐使用 pcntl_signal_dispatch()
// 注册信号处理
pcntl_signal(SIGTERM, function ($signo) {
echo "收到 SIGTERM 信号,清理资源...\n";
// 关闭数据库连接、释放文件句柄等
exit; // 或自己决定是否退出
});
pcntl_signal(SIGINT, function ($signo) {
echo "收到 Ctrl+C (SIGINT),正在退出...\n";
exit;
});
echo "PID: " . posix_getpid() . "\n";
echo "等待信号...\n";
while (true) {
// 推荐方式:手动派发待处理的信号
if (function_exists('pcntl_signal_dispatch')) {
pcntl_signal_dispatch();
}
sleep(1);
}
⚠️ 注意:
- 必须在 CLI 模式下运行(
php script.php),Web 模式(FastCGI/FPM)通常不直接接收信号。 declare(ticks = 1)在 PHP 7.1+ 已被弃用,推荐使用pcntl_signal_dispatch()手动触发信号分发。
使用 pcntl_signal_dispatch() 检查信号
适合在 长循环、事件循环 中定期检查是否有待处理的信号。
<?php
pcntl_signal(SIGTERM, function () {
fwrite(STDERR, "收到 SIGTERM,准备退出...\n");
exit;
});
while (true) {
// 你的业务逻辑...
usleep(100000); // 0.1秒
// 每次循环检查并处理信号
pcntl_signal_dispatch();
}
使用 pcntl_async_signals()(PHP 7.1+)
更现代的方式:开启异步信号处理,无需手动 dispatch。
<?php
pcntl_async_signals(true); // 启用异步信号处理
pcntl_signal(SIGTERM, function ($signo) {
echo "Async SIGTERM received, cleaning up...\n";
exit;
});
echo "Running... PID: " . posix_getpid() . "\n";
while (true) {
// 不需要调用 dispatch,系统自动回调
sleep(1);
}
优点:简洁、高效,推荐在 PHP 7.1+ 使用。
常见终止信号说明
| 信号 | 值 | 默认行为 | 触发方式 |
|---|---|---|---|
| SIGTERM | 15 | 终止进程 | kill <pid |
| SIGINT | 2 | 终止进程 | Ctrl+C |
| SIGQUIT | 3 | 终止+core dump | Ctrl+\ |
| SIGHUP | 1 | 终止(或重读取配置) | 终端断开、kill -1 |
| SIGKILL | 9 | 强制杀死 | kill -9 <pid>(无法捕获) |
你无法捕获
SIGKILL和SIGSTOP。
实际应用场景
- 守护进程(Daemon):收到
SIGTERM时,等待当前任务完成后再退出。 - Web Worker(如 ReactPHP、Swoole):收到信号后停止接受新请求,等已有请求处理完毕再关闭。
- 脚本长时间运行:允许用户通过
Ctrl+C优雅退出。
必须在 CLI 下运行
上述所有信号处理仅在 PHP CLI(命令行)下有效。
在 PHP-FPM 或 Apache mod_php 中,进程由服务器管理,通常不能直接捕获信号(因为它们来自 FastCGI 池管理器)。
完整示例:带资源清理的守护进程
<?php
pcntl_async_signals(true);
$running = true;
// 处理终止信号
$cleanup = function () use (&$running) {
fwrite(STDERR, "SIGTERM received. Graceful shutdown...\n");
// 关闭数据库连接、写最后的日志等
$running = false;
};
pcntl_signal(SIGTERM, $cleanup);
pcntl_signal(SIGINT, $cleanup);
echo "Server PID: " . posix_getpid() . "\n";
while ($running) {
// 模拟处理任务
echo "Working...\n";
sleep(2);
}
echo "Bye!\n";
想测试的话,可以:
php daemon.php & # 得到 PID 后,执行 kill <PID> # 发送 SIGTERM # 或按 Ctrl+C 如果在前台运行
- 核心函数:
pcntl_signal()+pcntl_signal_dispatch()或pcntl_async_signals(true)。 - 仅 CLI 模式 有效。
- 常见信号:SIGTERM(kill)、SIGINT(Ctrl+C)。
- 无法捕获:SIGKILL、SIGSTOP。