c++如何捕获Ctrl+C信号_c++ signal信号处理【示例】

10次阅读

Ctrl+C触发SIGINT信号,c++程序需用signal()或sigaction()拦截处理;推荐sigaction()因其更可靠、不自动重置且支持精细控制;信号处理函数中仅能调用异步信号安全函数。

c++如何捕获Ctrl+C信号_c++ signal信号处理【示例】

Ctrl+C 会触发 SIGINT,用 signal()sigaction() 注册处理函数

按下 Ctrl+C 默认终止进程,但 C++ 程序可通过系统信号机制拦截。linux/macOS 下它发送 SIGINT(值为 2),windows 控制台也支持(尽管信号语义略有不同)。关键不是“C++ 原生支持”,而是调用 POSIX/C 运行时接口——所以必须用 signal() 或更可靠的 sigaction(),不能靠 try/catch

signal() 简单但有陷阱:重入、不可移植、自动重置

常见写法是 signal(SIGINT, handler),但要注意:

  • handler 函数只能调用异步信号安全函数(如 write()_exit()),不能用 coutmallocprintf —— 否则行为未定义
  • 某些系统(如旧版 glibc)在进入 handler 后会自动将信号处理重置为默认(SIG_DFL),导致第二次 Ctrl+C 直接退出
  • 不保证 handler 执行期间屏蔽同信号,可能被重复中断(虽 SIGINT 通常不会快速连按)

示例(仅限简单场景):

#include  #include   void sigint_handler(int) {     write(1, "Caught SIGINTn", 14); // 安全:用 write 而非 cout     _exit(0); // 安全:不用 exit()(会调 cleanup) }  int main() {     signal(SIGINT, sigint_handler);     while (true) {} // 等待信号 }

推荐用 sigaction():可屏蔽信号、不重置、可设标志

sigaction() 是 POSIX 标准方式,控制粒度更细:

立即学习C++免费学习笔记(深入)”;

  • 通过 sa_flags 可设 SA_RESTART(系统调用被中断后自动重试)、SA_nodeFER(handler 中不屏蔽本信号)等
  • sa_mask 显式指定 handler 执行期间要屏蔽的其他信号(防竞态)
  • 注册后不会自动恢复为默认行为,避免意外退出

示例(更健壮):

#include  #include   void sigint_handler(int sig) {     write(1, "SIGINT receivedn", 16);     _exit(0); }  int main() {     struct sigaction sa;     sa.sa_handler = sigint_handler;     sigemptyset(&sa.sa_mask);     sa.sa_flags = SA_RESTART; // 防 read()/sleep() 被打断后失败     sigaction(SIGINT, &sa, nullptr);     pause(); // 等待信号(比 busy-loop 更省 CPU) }

线程程序中,信号只发给「某个线程」,需主动管理

POSIX 规定:信号递送给进程中任意一个未屏蔽该信号的线程。这带来两个实际问题:

  • 主线程注册了 handler,但 SIGINT 可能被子线程接收 → handler 不执行
  • 多个线程都未屏蔽 SIGINT,可能多个线程同时进入 handler(危险)

通用做法:在主线程中屏蔽所有信号(pthread_sigmask(SIG_BLOCK, &set, nullptr)),再用 sigwait() 在专用线程里同步等待;或用 signalfd()(Linux 特有)把信号转为文件描述符事件。不要依赖主线程天然收到信号。

真正难的不是注册 handler,而是确保 handler 里只做最简操作(比如设个 volatile sig_atomic_t flag),然后由主逻辑轮询退出;否则一不留神就踩进信号不安全函数的坑里。

text=ZqhQzanResources