核心转储文件是程序崩溃瞬间的内存快照,含栈、堆、寄存器、信号等信息;需先执行ulimit -c unlimited并用-g编译,再通过gdb ./program core加载分析,用bt查看调用栈、info registers检查寄存器、x/读内存定位段错误根因。

崩溃后用 gdb ./program core 加载 core 文件
段错误发生时,程序会终止并(如果启用)生成 core 文件。GDB 本身不捕获运行时崩溃,它只负责事后分析现场。所以第一步不是启动 GDB 再 run,而是确保有可用的 core 文件:
– 先执行 ulimit -c unlimited(当前 shell 有效)
– 编译时必须带 -g,否则 bt 只能显示地址,看不到函数名和行号
– 运行程序触发段错误,确认生成了 core(如 core.12345)
– 然后执行 gdb ./your_program core.12345
bt 是核心命令,但要注意默认只显示当前线程栈帧
bt(或 backtrace)输出的是崩溃瞬间的调用栈,最上面的 #0 就是出错位置。但容易忽略两点:
– 如果程序是多线程的,bt 默认只显示当前线程(通常是触发信号的那个),需先用 info threads 查所有线程,再用 thread N 切换,然后对每个可疑线程单独 bt
– bt 有时被优化干扰(比如 -O2 下内联过多),导致栈帧缺失或跳转异常;此时可加 bt full 尝试补全局部变量上下文,或干脆重新用 -O0 -g 编译复现
– 若 bt 显示 ?? 或只有地址(如 0x7f8a1f5a4520),说明对应符号未加载,检查是否用了 strip、或链接了无调试信息的库
光看 bt 不够,得结合 info registers 和 x/ 查内存访问意图
段错误的本质是 CPU 尝试执行一条非法内存访问指令,bt 告诉你“在哪崩”,但不告诉你“为什么崩”。这时候要查:
– info registers:看 rip(崩溃指令地址)、rsi/rdi(常见参数寄存器)、rax(返回值或临时寄存器)是否为 0 或明显非法地址(如 0x1、0xfffffffffffffffe)
– x/4wx $rdi(举例):用 x/ 直接读取疑似野指针指向的内存,若报 Cannot access memory at address ...,基本坐实解引用空/野指针
– disassemble:查看 $rip 附近几条汇编,确认崩溃指令是 mov、call 还是 lea,有助于判断是读、写还是跳转引发的 fault
没有 core 文件时,用 catchsegv 或 ASan 替代
线上环境常禁用 core,或程序一闪而过来不及抓,这时 gdb --pid 并不可靠。更实用的替代方案:
– catchsegv ./your_program:无需改代码,直接包装运行,崩溃时自动打印简易堆栈(不含变量,但快且轻量)
– gcc -fsanitize=address -g 编译:ASan 会在第一次非法访问时立即中止,并给出精确到行、访问类型(read/write)、越界偏移的报告,比 bt 更早暴露问题根源
– 注意:ASan 与 valgrind 不兼容,两者不要同时用;且 ASan 会显著增大内存占用,不适合超大进程
bt 看起来“合理”,但实际执行路径早已偏移。遇到这种 case,别只盯着 bt 输出,先验证崩溃点的寄存器值和内存状态是否符合预期。


















