跨平台信号处理需分平台实现:windows用setconsolectrlhandler(仅主线程有效),linux用sigaction(禁用signal);回调中禁止异步不安全函数,需幂等处理并避免直接exit。

Windows下用SetConsoleCtrlHandler捕获Ctrl+C,但Linux不认这个函数
Windows没有SIGINT的POSIX语义,直接调signal(SIGINT, ...)在控制台程序里往往收不到Ctrl+C;Linux则根本没SetConsoleCtrlHandler。跨平台信号处理不是“写一次,到处跑”,得按系统分路径处理。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用预处理器判断平台:
#ifdef _WIN32走SetConsoleCtrlHandler,否则走signal或sigaction - Windows侧必须在主线程调用
SetConsoleCtrlHandler,子线程注册无效 - Linux侧优先用
sigaction而非signal,因为后者在不同glibc版本行为不一致,且不支持阻塞信号集 - 不要在信号处理函数里调
std::cout、malloc、printf等非异步信号安全函数——Windows的控制台处理回调同理,只能调ExitProcess、WriteConsoleA等少数API
sigaction和signal在Linux上表现差异大
很多老代码用signal(SIGINT, handler),看似能工作,但实际有隐患:比如第二次Ctrl+C可能恢复默认行为(终止进程),或者被其他信号中断后无法重入。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 始终用
sigaction替代signal,显式设置sa_flags = SA_RESTART | SA_NOCLDSTOP - 把要屏蔽的信号填进
sa_mask,避免处理期间被其他信号打断(例如在清理资源时被SIGTERM插进来) - Linux下
SIGINT默认是终端前台进程组接收,后台进程或守护进程需额外处理会话/进程组关系,否则收不到
Windows控制台退出时,SetConsoleCtrlHandler回调可能被多次触发
用户快速连按Ctrl+C、或从IDE点击“停止”按钮,可能触发多次CTRL_C_EVENT。回调函数如果没做幂等处理,容易重复释放资源、double delete、或触发二次exit()崩溃。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用静态
bool标志位做首次检查,如static bool handled = false; if (handled) return TRUE; - 回调中避免调用复杂逻辑,只设标志+调
SetEvent(配合主线程WaitForSingleObject)或写管道通知 - 不要在回调里直接调
exit()——它可能绕过atexit注册的清理函数,C++对象析构也不保证执行
跨平台信号统一抽象时,别忽略SIGQUIT和SIGTERM的语义差异
Linux下SIGQUIT(Ctrl+\)默认产生core dump,SIGTERM是常规终止信号;Windows根本没有对应信号,SetConsoleCtrlHandler只暴露CTRL_C_EVENT、CTRL_BREAK_EVENT、CTRL_CLOSE_EVENT等几个事件,且CTRL_CLOSE_EVENT在GUI子系统启动后可能被静默吞掉。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 别强行把
CTRL_CLOSE_EVENT映射成SIGTERM——它更接近进程被任务管理器“结束任务”,应视为不可恢复的强制退出 - Linux端若需模拟
CTRL_CLOSE_EVENT语义(如窗口关闭),建议用kill(getpid(), SIGTERM)+ 自定义清理流程,而非依赖信号名对齐 - 日志里记录实际触发的源类型(
"SIGINT via terminal"vs"CTRL_C_EVENT from console"),方便排查环境差异
跨平台信号最麻烦的不是注册方式,而是“什么时候能安全退出”——Windows控制台事件可能在DLL卸载中途到来,Linux的sigaction掩码若没配好,主线程可能卡在read()里收不到信号。这些边界情况,光靠抽象层封装压不住,得在每个平台验证退出路径的原子性。









