C++怎么使用信号处理_C++异常中断教程【系统】

2次阅读

signal()注册后不生效的根本原因是posix下行为不一致:linux默认重置为sig_dfl且不重启系统调用;应优先使用sigaction()并设置sa_restart等标志,避免在信号处理函数中调用非异步信号安全函数。

C++怎么使用信号处理_C++异常中断教程【系统】

signal() 函数注册处理函数后不生效?

常见现象是调用 signal(SIGint, handler) 后按 Ctrl+C 没反应,或只触发一次就恢复默认行为。根本原因是 POSIX 标准下 signal() 行为在不同系统(尤其是 Linux vs macos)差异大:Linux 默认重置信号处理为 SIG_DFL,且不保证信号中断的系统调用自动重启。

  • 优先改用 sigaction() —— 它明确、可移植、支持屏蔽信号集和标志位
  • 若必须用 signal(),在 Linux 上需配合 siginterrupt(SIGINT, 0) 禁用自动重置(但不推荐)
  • 注意:signal()c++ 中注册的函数不能是类成员函数(非静态),也不能带捕获的 Lambda;必须是 C 风格自由函数或 Static 成员函数

在 C++ 类里安全绑定信号处理逻辑?

直接把 handler 写成普通函数会丢失对象上下文,而传 this 指针又违反 C ABI 要求(信号处理函数只能接受 int 参数)。真正可行的做法是用静态转发 + 全局/静态指针桥接,但必须加锁或限制为单例场景。

  • 声明静态成员函数作为信号入口:static void sig_handler(int sig) { instance->on_signal(sig); }
  • std::atomicstd::mutex 保护 instance 赋值(信号可能在构造中途到达)
  • 禁止在信号处理函数中调用 std::coutmallocstd::String::append 等非异步信号安全函数 —— 只能用 write()siglongjmp()atomic_store

signal() 和 sigaction() 的关键参数差异

sigaction()sa_flags 字段决定了行为是否符合预期,而 signal() 隐含了某些不可控标志。比如想让被中断的 read() 自动重试,就必须设 SA_RESTART;想屏蔽其他信号在 handler 执行期间送达,就得填 sa_mask

  • signal(SIGINT, h) 等价于 sigaction(SIGINT, &{.sa_handler=h, .sa_flags=0}, nullptr) —— 无重试、无掩码、不可靠
  • 正确写法示例:
    struct sigaction sa; sa.sa_handler = h; sa.sa_flags = SA_RESTART; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, nullptr);
  • 不要忽略旧 handler 返回值 —— sigaction() 可用于临时保存并恢复原 handler(如拦截 SIGPIPE 但不干扰其他模块)

为什么 catch(…) 捕获不到 SIGSEGV?

C++ 异常和 unix 信号是两套机制:catch 只能捕获 throw 抛出的异常,而 SIGSEGV 是内核发给进程的异步事件,不会触发展开或异常传播。强行用 setjmp/longjmp 在信号 handler 里跳转,容易破坏 RAII 和栈平衡,属于高危操作。

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

  • 调试阶段用 gdb 捕获 SIGSEGV 更可靠:handle SIGSEGV stop print
  • 生产环境应避免依赖信号兜底 —— 用 std::shared_ptr 防悬挂指针、std::vector::at() 替代 []、启用 ASan 编译选项提前暴露问题
  • 极少数需要信号转异常的场景(如嵌入式 watchdog),必须确保 handler 中只做原子标记,再由主循环检查并 throw

信号处理最棘手的不是注册失败,而是 handler 执行时的上下文不可控 —— 它可能打断任意一行代码,包括 STL 内部锁、内存分配器临界区、甚至另一个信号 handler。别试图在 signal handler 里“做点事”,只做最小化、可重入、无副作用的状态标记。

text=ZqhQzanResources