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