Appearance
crash 信号 demo —— 每种信号一个可跑的最小复现
把 crash/signals.md 里讲的各种导致 crash 的信号,做成一个场景一个可编译运行的最小程序。跑一遍就能亲眼看到"这段代码触发了 SIGxxx、退出码是 128+信号号"。
跨平台:Linux(gcc/g++) 与 macOS(clang) 都能编译运行。但堆检测类(double free / 堆溢出)依赖 allocator 实现,Linux/glibc 上现象最标准,macOS 的 libmalloc 行为不同——见下方"平台差异"。
一分钟上手
bash
make # 编译全部 10 个 demo
make run # 逐个运行,打印每个的退出码并翻译成信号名(带超时保护,不会卡住)
make cleanmake run 输出形如:
bash
---- segv_null 退出码 139 => 被信号 11 (SIGSEGV) 杀死
---- abrt_double_free 退出码 134 => 被信号 6 (SIGABRT) 杀死
---- fpe_divzero 退出码 136 => 被信号 8 (SIGFPE) 杀死
...demo 一览
| demo | 触发信号(Linux 预期) | 根因 |
|---|---|---|
segv_null.c | SIGSEGV | 空指针解引用 *(int*)0 = 1 |
segv_wild.c | SIGSEGV | 野指针(未初始化指针)写 |
segv_readonly.c | SIGSEGV | 写字符串字面量(只读 .rodata 段) |
segv_stackoverflow.c | SIGSEGV | 无限递归撑爆栈、撞 guard page |
abrt_double_free.c | SIGABRT | double free(重点:见文件头详解 chunk/freelist 机制) |
abrt_heap_overflow.c | SIGABRT | 堆越界写坏 chunk 元数据 → 下次 free 校验失败 abort |
abrt_assert.c | SIGABRT | assert() 失败 |
abrt_uncaught.cpp | SIGABRT | C++ 未捕获异常 → std::terminate → abort |
fpe_divzero.c | SIGFPE | 整数除以 0 |
ill_badfnptr.c | SIGILL / SIGSEGV | 调用一个乱指的函数指针 |
每个源文件的头部注释都详细写了"预期信号 + 为什么崩",尤其 abrt_double_free.c 讲透了 double free 破坏 freelist、glibc 校验时才 abort 的机制。
退出码对照(128 + 信号号)
进程被信号 N 杀死,shell 的 $? = 128 + N:
| 退出码 | 信号 | 含义 |
|---|---|---|
| 134 | 6 (SIGABRT) | 主动 abort(assert/double free/未捕获异常) |
| 136 | 8 (SIGFPE) | 算术异常(整数除零) |
| 138 | 10 (SIGBUS) | 总线错误(macOS 写只读段常报这个) |
| 139 | 11 (SIGSEGV) | 段错误(空指针/野指针/栈溢出) |
| 132 | 4 (SIGILL) | 非法指令 |
平台差异(实测)
同一份 UB 代码,不同平台/编译器可能给不同信号、甚至不崩——这正是 UB "没有标准答案"的体现。本机 macOS(x86_64/clang)实测:
| demo | Linux/glibc 预期 | macOS 实测 | 说明 |
|---|---|---|---|
| segv_null | SIGSEGV | ✅ SIGSEGV | 一致 |
| segv_wild | SIGSEGV | ⚠️ 有时不崩 | 野指针垃圾值恰好落在可写内存 → UB 不触发 |
| segv_readonly | SIGSEGV | ⚠️ SIGBUS | macOS 写 __TEXT 段报总线错误而非段错误 |
| segv_stackoverflow | SIGSEGV | ✅ SIGSEGV | 一致 |
| abrt_double_free | SIGABRT | ✅ SIGABRT | 一致(文案不同:libmalloc vs glibc) |
| abrt_heap_overflow | SIGABRT | ⚠️ 有时不崩 | macOS libmalloc 没当场抓到小越界 |
| abrt_assert | SIGABRT | ✅ SIGABRT | 一致 |
| abrt_uncaught | SIGABRT | ✅ SIGABRT | 一致 |
| fpe_divzero | SIGFPE | ✅ SIGFPE | 一致 |
| ill_badfnptr | SIGILL/SIGSEGV | ⚠️ SIGSEGV | 落点无执行权限 → SEGV(见 signals.md 2.5) |
结论:堆检测类(double free / heap overflow)和只读段写,想看最标准的现象请在 Linux 上跑。
segv_wild/heap_overflow偶尔不崩,恰恰印证了"UB 不保证一定崩,不崩时是静默数据损坏,比崩更可怕"。
深入排查
- 想还原崩溃现场(core + GDB)→ ../../crash/core-dump.md
- 每个信号的完整根因、
dmesg/退出码/GDB 定位 → ../../crash/signals.md - 用工具在崩之前就抓到(尤其堆错误)→ ASan(../../crash/asan.md)/ Valgrind(../../crash/valgrind.md)