用 signal() 可捕获 ctrl+c(sigint)并执行清理,但仅限异步信号安全函数;推荐跨平台使用 sigaction()(posix)或 windows 的 setconsolectrlhandler()。

如何用 signal() 捕获 Ctrl+C(SIGINT)
Ctrl+C 默认会终止进程,但你可以用 signal() 注册自定义处理函数来拦截它。关键不是“阻止退出”,而是“在退出前做清理”。signal() 是最轻量、最兼容的方式,适用于大多数控制台程序。
-
signal(SIGINT, handler)必须在main()开头尽早调用,否则可能错过首次信号 - 处理函数
handler只能调用异步信号安全函数(如write()、_exit()),不能用printf()、std::cout或new - 注册后,再次 Ctrl+C 仍会触发该 handler;若想只响应一次,需在 handler 内重置为
SIG_DFL或SIG_IGN
示例:
void sigint_handler(int) {
write(STDOUT_FILENO, "Caught SIGINT, cleaning up...\n", 32);
// 做资源释放、日志落盘等
_exit(0); // 不要用 exit(),它不安全
}
int main() {
signal(SIGINT, sigint_handler);
while (true) { /* 主循环 */ }
}
sigaction() 比 signal() 更可靠
signal() 在不同系统上语义不一致(比如是否自动重置 handler),而 sigaction() 行为确定、可屏蔽其他信号、支持更多选项,是更现代的推荐做法。
- 必须用
struct sigaction初始化,不能只传函数指针 -
sa_flags设为SA_RESTART可让被中断的系统调用自动重试(避免read()等突然返回EINTR) -
sa_mask可临时阻塞其他信号(如屏蔽SIGTERM防止嵌套中断)
注意:C++11 及以后标准不保证 signal() 的可移植性,sigaction() 是 POSIX 标准接口,Linux/macOS 都支持,Windows 需用 SetConsoleCtrlHandler() 替代。
Windows 下不能用 signal() 捕获 Ctrl+C
Windows 控制台对 Ctrl+C 的处理机制不同:signal(SIGINT, ...) 在 MinGW 或 MSVC 下可能无效或行为异常。必须改用 Windows API SetConsoleCtrlHandler()。
- handler 函数签名是
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType) -
dwCtrlType可能是Ctrl+C、Ctrl+Break、关闭窗口等,需显式判断dwCtrlType == CTRL_C_EVENT - 返回
TRUE表示已处理,系统不再执行默认终止;返回FALSE则继续默认行为
跨平台程序建议封装一层:Linux/macOS 用 sigaction(),Windows 用 SetConsoleCtrlHandler(),避免混用。
常见陷阱:异步信号安全与资源竞争
信号是异步的,可能在任意时刻中断你的代码。这意味着:
- 不能在 handler 里调用 STL 容器、智能指针、
malloc()、std::string::c_str()—— 这些都不是异步信号安全的 - 全局变量如果被主逻辑和 handler 同时修改,必须用
volatile sig_atomic_t类型(仅此一种保证原子读写的类型) - 不要依赖 RAII 在信号中自动析构——栈展开不会发生,
std::unique_ptr的 destructor 不会被调用
真正安全的做法是:handler 中只设一个 volatile sig_atomic_t flag = 0,主循环定期检查该 flag 并主动退出。这才是可控、可测试、可调试的方式。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











