跨平台信号处理需分层实现:上层定义统一信号枚举,底层按平台分别实现,仅支持INT/TERM/QUIT交集信号;handler仅原子写入信号ID,由专用线程消费;避免异步信号不安全操作。

信号注册与回调绑定必须绕过平台差异
Linux 用 sigaction,Windows 用 SetConsoleCtrlHandler,两者接口和语义完全不同。硬写一套统一抽象容易漏掉关键细节,比如 Windows 不支持 SIGUSR1 这类信号,而 Linux 的 SIGCHLD 在 Windows 上根本不存在。直接封装成“跨平台 signal()”是陷阱。
实际做法是分层:上层定义统一的信号枚举(如 Signal::INT、Signal::TERM),底层按平台分别实现注册逻辑,且只支持交集信号(INT、TERM、QUIT)。Windows 部分忽略所有非控制台信号,Linux 部分对不支持的信号返回错误而非静默忽略。
-
sigaction必须设SA_RESTART和SA_SIGINFO,否则read()等系统调用可能被中断后不重试 - Windows 的
SetConsoleCtrlHandler回调必须返回TRUE才能阻止默认终止行为,返回FALSE会直接退出进程 - 避免在信号处理函数里调用
std::cout、malloc、new—— 它们不是异步信号安全的
回调执行不能依赖主线程调度
信号处理函数(handler)运行在内核上下文,不能做复杂操作。常见错误是把业务逻辑直接塞进 handler,结果触发死锁或内存损坏。正确方式是 handler 只做一件事:往一个线程安全的队列里写入信号 ID,然后由专用线程消费。
推荐用 std::atomic_flag + std::queue + std::mutex 组合,但注意 std::queue::push() 不是 async-signal-safe,所以改用 lock-free ring buffer(如 moodycamel::ConcurrentQueue)或更简单的方案:handler 仅设置一个 std::atomic<int></int> 标志位,主循环定期轮询。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- Linux 下可配合
signalfd()(需 kernel ≥2.6.27)把信号转为文件描述符,用epoll统一等待,但 Windows 不支持,故不作为默认路径 - Windows 下若程序无控制台(如 GUI 或服务),
SetConsoleCtrlHandler失效,需改用RegisterServiceCtrlHandler或监听窗口消息,这类场景应明确提示用户限制 - 不要用
std::condition_variable在 handler 里 notify —— 它不是信号安全的
资源清理时机必须与信号语义对齐
收到 Signal::TERM 表示“请尽快退出”,而 Signal::INT 常用于调试中断。两者都该触发 cleanup,但 TERM 后通常要等子资源释放完成才真正 exit,INT 可能允许立即终止。框架不应替用户决定是否阻塞等待。
提供两个接口:on_signal(signal, std::function<void> handler)</void> 用于快速响应;on_signal_blocking(signal, std::function<void>)> handler)</void> 允许传入一个 “done callback”,当 cleanup 完成后调用它,由用户决定何时调用 exit() 或继续运行。
- 避免在 handler 中调用
std::exit()—— 它会跳过局部对象析构和atexit注册函数 - 若使用 RAII 资源管理器(如数据库连接池),确保其析构函数是 noexcept 且不抛异常,否则
std::terminate会被触发 - 多线程环境下,
pthread_cancel已废弃,别用;改用协作式取消(如检查原子标志位)
CMake 构建时需隔离平台逻辑
不能靠 #ifdef _WIN32 把所有平台代码揉在一个 .cpp 文件里。这样会导致编译器无法优化、单元测试难覆盖、且 Windows 开发者看不到 Linux 路径的问题。
按平台拆文件:signal_linux.cpp、signal_windows.cpp,CMake 中用 target_sources 按条件添加:
if(WIN32) target_sources(signal_lib PRIVATE signal_windows.cpp) else() target_sources(signal_lib PRIVATE signal_linux.cpp) endif()
- 公共头文件
signal.hpp只暴露统一接口,不包含任何平台头(如signal.h或windows.h) - Linux 版本需链接
-lrt(用于timerfd_create等扩展),Windows 版本需定义_CRT_SECURE_NO_WARNINGS避免警告 - CI 流水线必须同时跑 Linux 和 Windows 构建,且至少各跑一次信号触发测试(如用
kill -TERM或GenerateConsoleCtrlEvent)
跨平台信号框架最难的不是注册或回调,而是让不同平台的“退出语义”在用户代码里保持一致 —— 这需要文档写清楚每种信号的实际行为边界,而不是假装它们完全等价。

















