C++如何捕获段错误Signal _ signal与sigaction处理【实战】

阿辰吖_2334

阿辰吖_2334

2026-04-08

716人浏览

原创

signal() 失效因不可靠:自动重置为默认行为、无信号屏蔽、不传siginfo_t,且调用非异步安全函数易二次崩溃;应改用sigaction()配sa_siginfo与sa_onstack,并用_exit()终止。

c++如何捕获段错误signal _ signal与sigaction处理【实战】

段错误发生时,signal() 为什么经常失效?

因为 signal() 在多数 Linux 系统上是不可靠的:它会将信号处理函数重置为默认行为(比如 SIGSEGV 又变回终止进程),且不保证信号屏蔽、无法传递 siginfo_t。更糟的是,若在信号处理中调用非异步信号安全函数(如 printf、malloc),会导致二次崩溃。

实操建议:

  • 永远不要用 signal() 处理 SIGSEGV、SIGBUS 等致命信号
  • 若必须兼容老代码,至少用 siginterrupt(SIGSEGV, 0) 防止系统调用被中断后不重启
  • 真正可靠的起点是 sigaction(),它原子地安装 handler 并控制标志位

sigaction 捕获 SIGSEGV 的最小可靠写法

关键不是“能进 handler”,而是“进得安全、能诊断、不破坏上下文”。以下是最小但生产可用的骨架:

struct sigaction sa;
sa.sa_sigaction = [](int sig, siginfo_t* info, void* uctx) {
    ucontext_t* context = static_cast<ucontext_t>(uctx);
    // 注意:只调用 async-signal-safe 函数!
    write(STDERR_FILENO, "SIGSEGV caught\n", 17);
    // 打印出错地址(最常需信息)
    char addr_buf[32];
    int len = snprintf(addr_buf, sizeof(addr_buf), "addr=%p\n", info->si_addr);
    write(STDERR_FILENO, addr_buf, len);
    _exit(128 + SIGSEGV); // 不 return,不调用 exit()
};
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // 必须开 SA_SIGINFO 才能拿到 info
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, nullptr);
</ucontext_t>

注意点:

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
  • SA_ONSTACK 很重要——段错误可能发生在栈溢出时,没独立信号栈就直接 crash
  • 必须用 sa_sigaction 而非 sa_handler,否则拿不到 si_addr 和寄存器上下文
  • _exit() 是唯一安全的终止方式;return 或 exit() 可能触发清理逻辑再次访问非法内存

从 siginfo_t 里能挖出什么真实线索?

siginfo_t 是调试段错误的核心数据源,但容易被忽略字段:

  • info->si_code 区分原因:SEGV_MAPERR(地址无映射)、SEGV_ACCERR(权限错误,如往只读页写)
  • info->si_addr 是触发访问的虚拟地址,可结合 /proc/self/maps 判断是否在堆/栈/库区间
  • 配合 ucontext_t 中的 uc_mcontext.gregs[REG_RIP](x86_64)可定位精确指令地址
  • 注意:si_code == SI_USER 表示是 kill() 发的,不是真段错误——别误判

为什么加了 handler 还是看不到崩溃现场?

捕获成功 ≠ 调试成功。常见断点失效、core dump 缺失、日志乱序,本质是干扰了系统默认行为:

  • 默认 SIGSEGV 会生成 core 文件,但自定义 handler 后需手动 raise(SIGABRT) 或调用 abort() 触发 dump(前提是 ulimit -c 已设)
  • GDB 下,若用 handle SIGSEGV stop print,handler 仍会执行;想跳过 handler 直接停在 fault 指令,得用 handle SIGSEGV nostop noprint pass
  • 多线程下,SIGSEGV 默认只发给出错线程,但 sigaction 是进程级设置,所有线程共享 handler——确保线程安全

真正难的从来不是注册 handler,而是在信号上下文中安全地提取、序列化、落盘足够多的上下文信息,又不引发二次故障。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
python中print函数的用法
python中print函数的用法

python中print函数的语法是“print(value1, value2, ..., sep=' ', end='\n', file=sys.stdout, flush=False)”。本专题为大家提供print相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.27

2640

5

python print用法与作用
python print用法与作用

本专题整合了python print的用法、作用、函数功能相关内容,阅读专题下面的文章了解更多详细教程。

2026.02.03

249

24

printf用法大全
printf用法大全

php中文网为大家提供printf用法大全,以及其他printf函数的相关文章、相关下载资源以及各种相关课程,供大家免费下载体验。

2023.06.20

708

5

fprintf和printf的区别
fprintf和printf的区别

fprintf和printf的区别在于输出的目标不同,printf输出到标准输出流,而fprintf输出到指定的文件流。根据需要选择合适的函数来进行输出操作。更多关于fprintf和printf的相关文章详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.28

1009

10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

5287

5

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.10

2328

6

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

5287

5

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.10

2328

6

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

3898

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Conan 2 Essentials 免费课程
Conan 2 Essentials 免费课程

共0课时 | 0人学习

CMake 与 Conan 集成实践
CMake 与 Conan 集成实践

共0课时 | 0人学习

Conan 2 高级依赖模型介绍
Conan 2 高级依赖模型介绍

共0课时 | 0人学习