STTNet 0.7.0

第 17 章:Linux 信号与优雅退出

本章列出常见 Linux 信号及其编号,并说明异步信号处理与安全资源清理的边界。

1. 为什么使用同步等待

异步 signal handler 中只能调用极少数 async-signal-safe 函数。C++ 析构、锁、日志、内存分配和 OpenSSL 清理都不安全。STTNet 的退出模型在线程创建前屏蔽终止信号,并由主线程通过 sigwait 模型同步等待。

2. Linux 常见信号编号

信号Linux 编号含义与处理
SIGHUP1终端断开/常用于请求重载;STTNet 不自动处理
SIGINT2Ctrl-C;waitForTerminationSignal() 会等待
SIGQUIT3退出并可能生成 core;不作为正常退出信号
SIGABRT6abort();保留默认致命行为
SIGKILL9强制终止,不能捕获、阻塞或清理
SIGSEGV11非法内存访问;保留默认行为与 core dump
SIGPIPE13写入已关闭管道/socket;setExceptionHandling() 忽略
SIGTERM15标准终止请求;waitForTerminationSignal() 会等待
SIGCHLD17子进程状态变化;进程管理代码会涉及
SIGSTOP19强制暂停,不能捕获
编号说明:以上是 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. 退出流程

  1. setExceptionHandling()blockTerminationSignals() 在主线程中执行。
  2. 日志、Server、Reactor 和 Worker 对象随后完成创建。
  3. 监听服务启动。
  4. 主线程通过 waitForTerminationSignal() 同步等待。
  5. 收到 SIGINT 或 SIGTERM 后,server.close() 在正常线程上下文中执行。
异步 signal handler 的职责范围:delete Server、日志写入和加锁属于正常线程上下文;放入异步 handler 可能导致死锁、重复析构或堆状态损坏。

完整信号退出 Demo →