第 17 章:Linux 信号与优雅退出
本章列出常见 Linux 信号及其编号,并说明异步信号处理与安全资源清理的边界。
1. 为什么使用同步等待
异步 signal handler 中只能调用极少数 async-signal-safe 函数。C++ 析构、锁、日志、内存分配和 OpenSSL 清理都不安全。STTNet 的退出模型在线程创建前屏蔽终止信号,并由主线程通过 sigwait 模型同步等待。
2. Linux 常见信号编号
| 信号 | Linux 编号 | 含义与处理 |
|---|---|---|
| SIGHUP | 1 | 终端断开/常用于请求重载;STTNet 不自动处理 |
| SIGINT | 2 | Ctrl-C;waitForTerminationSignal() 会等待 |
| SIGQUIT | 3 | 退出并可能生成 core;不作为正常退出信号 |
| SIGABRT | 6 | abort();保留默认致命行为 |
| SIGKILL | 9 | 强制终止,不能捕获、阻塞或清理 |
| SIGSEGV | 11 | 非法内存访问;保留默认行为与 core dump |
| SIGPIPE | 13 | 写入已关闭管道/socket;setExceptionHandling() 忽略 |
| SIGTERM | 15 | 标准终止请求;waitForTerminationSignal() 会等待 |
| SIGCHLD | 17 | 子进程状态变化;进程管理代码会涉及 |
| SIGSTOP | 19 | 强制暂停,不能捕获 |
编号说明:以上是 STTNet 支持目标 Linux 上的常见编号。代码中使用
SIGTERM 等符号常量;数字仅用于说明 Linux 上的常见编号。3. 完整退出模型
using stt::system::ServerSetting;
// 这里设置 SIGPIPE 与致命信号的默认处理方式。
ServerSetting::setExceptionHandling();
// SIGINT (2) 与 SIGTERM (15) 在线程创建前完成屏蔽。
if(!ServerSetting::blockTerminationSignals())
return 1;
stt::network::HttpServer server;
server.setGracefulShutdownTimeout(5000);
if(!server.startListen(8083))
return 2;
// 主线程在正常 C++ 上下文中同步等待退出请求。
const int signalNumber = ServerSetting::waitForTerminationSignal();
if(signalNumber < 0)
return 3;
// close() 停止 accept,等待在途任务和发送队列,再释放资源。
return server.close() ? 0 : 4;4. SIGTERM 与 SIGKILL 的区别
SIGTERM (15) 表示有序终止请求,服务可停止接收新连接、完成在途请求并刷新日志;SIGKILL (9) 由内核立即终止进程,不执行析构、日志刷新或 finally。常见的进程监督流程在宽限期内使用 SIGTERM,超时后再使用 SIGKILL。
5. 退出流程
setExceptionHandling()和blockTerminationSignals()在主线程中执行。- 日志、Server、Reactor 和 Worker 对象随后完成创建。
- 监听服务启动。
- 主线程通过
waitForTerminationSignal()同步等待。 - 收到 SIGINT 或 SIGTERM 后,
server.close()在正常线程上下文中执行。
异步 signal handler 的职责范围:
delete Server、日志写入和加锁属于正常线程上下文;放入异步 handler 可能导致死锁、重复析构或堆状态损坏。