Ctrl+C在Windows和Linux上均触发SIGINT信号,但处理机制不同:Linux/macOS推荐sigaction(),Windows应使用SetConsoleCtrlHandler(),跨平台需统一通过轮询原子标志位实现。

Ctrl+C 在 Windows 和 Linux 上都触发 SIGINT
终端按下 Ctrl+C 时,操作系统会向当前前台进程发送 SIGINT 信号,不是 C++ 语言特性,而是 POSIX(Linux/macOS)和 Windows CRT(如 MSVC 运行时)都支持的通用机制。关键区别在于:Linux 默认终止进程,Windows 控制台默认也终止,但两者都允许你注册处理函数来拦截它。
用 signal() 注册简单处理函数,但有严重限制
signal() 是最基础的方式,适合快速响应、不涉及复杂逻辑的场景。但它在信号处理期间禁止调用大部分标准库函数(如 std::cout、malloc),且不能保证重入安全。常见错误是直接在里面打印日志或调用 std::exit() —— 这可能崩溃或死锁。
- 只允许调用异步信号安全函数(如
write()、_exit()) - 不要在 handler 中修改全局对象、调用 STL 容器方法或抛异常
- MSVC 的
signal()在 Windows 上对SIGINT支持较稳定;GCC/Clang 下推荐用sigaction()(见下条)
示例(仅用于演示,不推荐生产):
#include#include volatile std::sig_atomic_t g_stop_requested = 0; void signal_handler(int sig) { if (sig == SIGINT) { g_stop_requested = 1; // OK: sig_atomic_t 是原子读写类型 } } int main() { std::signal(SIGINT, signal_handler); while (!g_stop_requested) { // 做工作... } std::cout << "Exiting gracefully.\n"; }
用 sigaction() 更可靠(Linux/macOS 推荐)
sigaction() 提供更细粒度控制:可屏蔽其他信号、指定是否重启被中断的系统调用、避免信号处理函数被多次触发等。它不修改全局状态,行为可预测,是 POSIX 系统首选。
立即学习“C++免费学习笔记(深入)”;
- 必须用
struct sigaction显式配置,不能像signal()那样传函数指针就完事 - 设置
sa_flags |= SA_RESTART可让阻塞式系统调用(如read())在收到信号后自动重试,而不是返回 -1 +EINTR - 用
sigfillset(&act.sa_mask)可临时屏蔽所有信号,防止嵌套中断
注意:sigaction() 在 Windows MinGW 环境可用,但原生 MSVC 不支持 —— 如果跨平台,需条件编译。
Windows 控制台专用:SetConsoleCtrlHandler()
Windows 提供了比 signal() 更底层、更可控的 API:SetConsoleCtrlHandler()。它能捕获 CTRL_C_EVENT、CTRL_BREAK_EVENT、甚至关机前的 CTRL_SHUTDOWN_EVENT,且 handler 中可以安全调用多数 Win32 API(比如 SetEvent()、PostThreadMessage())。
- handler 函数必须是
BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)类型 - 返回
TRUE表示已处理,系统不再执行默认动作(即不退出);返回FALSE则继续传递给下一个 handler 或执行默认终止 - 不能在 handler 中调用
ExitProcess()或TerminateProcess(),否则会导致未定义行为
这是 Windows 下唯一能可靠响应 Ctrl+Break 或关机通知的方式,signal() 对后者完全无效。
真正麻烦的是跨平台统一处理:SIGINT 语义在不同系统上看似一致,但底层机制、线程安全性、可调用函数集差异极大。别试图写一个“通用 handler 函数”,应该按平台分路径,在主循环里轮询 g_stop_requested 这类标志位,把信号处理降级为“设标志”,其余逻辑放在正常线程流中执行。








