CoreDump原理和调试指南
CoreDump 原理和调试指南
从原理、配置到调试指南的完整手册。
一、CoreDump 逻辑链
CoreDump 的完整处理流程如下:
| 步骤 | 阶段 | 说明 |
|---|---|---|
| 1 | 触发 | 程序在用户态干了非法事(如空指针访问) |
| 2 | 捕获 | CPU 异常,强制切到内核态(此时还在该进程的上下文里) |
| 3 | 裁决 | 内核决定发 SIGSEGV,在进程的 pending 位图上做标记 |
| 4 | 返回 | 内核准备切回用户态 |
| 5 | 劫持 | 内核检查到位图标记,修改用户态栈和 PC 指针 |
| 6 | 执行 | 程序回到用户态,被迫执行你写的信号处理函数(或直接默认自杀) |
| 7 | 遗言 | 如果是默认自杀,内核在该进程上下文中执行 do_coredump,把内存写成 core 文件 |
| 8 | 终局 | 进程彻底消亡(do_exit),文件留在磁盘供 GDB 调试 |
二、触发 CoreDump 的主要信号
| 编号 | 信号名称 | 解释 |
|---|---|---|
| 11 | SIGSEGV |
非法内存引用(段错误) |
| 6 | SIGABRT |
程序主动调用 abort() |
| 4 | SIGILL |
非法指令 |
| 7 | SIGBUS |
总线错误 |
| 8 | SIGFPE |
算术运算异常 |
主动触发 CoreDump
# 方式一:使用 gcore 工具
gcore <PID>
# 方式二:发送信号
kill -3 <PID>
三、编译选项设置
为了后续 GDB 调试 coredump 文件,需要使用 -g 编译选项来生成带调试信息的可执行文件,同时建议加上 -O0 关闭优化,避免调试时变量被优化掉或行号错乱。
gcc -g -O0 -o myapp myapp.c
生产环境部署
为了避免带调试信息的可执行文件暴露源码信息,使用 strip 命令剥离调试符号,生成一个不带符号的版本部署到生产环境,同时保留带符号的原始文件用于事后调试。
# 剥离调试符号
strip -s myapp -o myapp-stripped
四、CoreDump 相关配置
4.1 开启 CoreDump 功能
方法一:永久配置
vi /etc/security/limits.conf
# 在文件末尾添加:
* soft core unlimited
* hard core unlimited
方法二:临时配置
ulimit -c unlimited # 只对当前 shell 生效
验证配置是否生效
# 查看当前 shell 是否生效
ulimit -c
# 查看应用进程是否生效
cat /proc/PID/limits
⚠️ 注意:开启 coredump 功能后,需要重新登录用户、重新启动应用进程。如果应用进程是由另外一个父应用进程 fork 启动的,需要重启其父应用进程。
4.2 配置存储路径与命名
格式说明
| 占位符 | 含义 |
|---|---|
%e |
进程名(executable name) |
%p |
进程 ID(PID) |
%t |
时间戳 |
%h |
主机名 |
自定义存储路径与命名
vi /etc/sysctl.conf
# 文件末尾添加:
kernel.core_pattern=/corefiles/core_%e_%p
# 让配置生效
sysctl -p /etc/sysctl.conf
# 查看配置结果
cat /proc/sys/kernel/core_pattern
临时修改(重启后失效)
sysctl -w kernel.core_pattern="/corefiles/core_%e_%p"
⚠️ 注意:
- 运行应用程序的用户必须对 core 文件的存储目录拥有写入权限
- 确保 core 文件存储目录所在的磁盘分区有足够的剩余空间
五、GDB 打开 CoreDump 文件
5.1 使用带符号的版本
gdb myapp-full-debug core.12345
5.2 使用 Stripped 版本 + 符号文件
# 1. 启动 GDB,加载主程序和 coredump
gdb myapp-stripped core.12345
# 2. 加载主程序符号文件
symbol-file myapp.symbol
# 3. 查看动态库加载地址
info sharedlibrary
# 输出示例:
# From To Syms Read Shared Object Library
# 0x00007f8a2a4a0000 0x00007f8a2a5c3a5f Yes /lib/x86_64-linux-gnu/libc.so.6
# 0x00007f8a2a2a0000 0x00007f8a2a3a1a5f Yes /usr/lib/libmylib.so
# 0x00007f8a2a0a0000 0x00007f8a2a1a1a5f Yes /lib/ld-linux-x86-64.so.2
# 4. 加载动态库的符号文件(需要指定基地址)
add-symbol-file libmylib.symbol 0x00007f8a2a2a0000
# 5. 查看当前加载的符号信息
info files # 显示所有已加载的符号文件
info sharedlibrary # 查看动态库加载状态
六、CoreDump 定位常用命令
6.1 常用命令
| 命令 | 说明 |
|---|---|
bt |
打印调用栈 |
f <栈帧编号> |
切换到指定栈帧 |
p <变量> |
打印指定变量的值 |
info locals 或 info l |
打印当前栈帧的所有局部变量 |
info args |
打印当前栈帧函数的所有参数 |
info frame |
打印当前栈帧的详细信息 |
6.2 多线程分析
| 命令 | 说明 |
|---|---|
info threads 或 info t |
列出所有线程 |
thread <线程编号> 或 t <线程编号> |
切换到指定线程 |
thread apply all bt |
打印所有线程的调用栈 |
6.3 内存检查
x 命令语法
x/<n/f/u> <addr>
| 参数 | 说明 |
|---|---|
n |
正整数,表示显示内存单元个数 |
f |
显示格式:x=十六进制,s=字符串,i=指令地址 |
u |
单元大小:b=单字节,h=双字节,w=四字节,g=八字节 |
示例
x/10i 0x555555555140 # 从某地址开始看 10 条指令
x/16xg 0x555555555140 # 以十六进制显示 16 个单元,一单元8个字节
其他内存命令
| 命令 | 说明 |
|---|---|
info proc mappings |
显示进程的虚拟内存地址空间映射表 |
6.4 查看寄存器
| 命令 | 说明 |
|---|---|
info registers 或 info reg |
显示所有寄存器 |
关键寄存器
| X86寄存器 | ARM寄存器 | 说明 |
|---|---|---|
rsp |
sp |
堆栈指针寄存器,指向当前堆栈帧的顶部 |
rbp |
fp/x29 |
栈帧基址指针寄存器,指向当前函数的栈帧的基址 |
rip |
pc |
正在执行的指令地址 |
6.5 反汇编
| 命令 | 说明 |
|---|---|
disassemble |
反汇编当前函数 |
disassemble <函数名> |
反汇编指定函数 |
disassemble /m <函数名> |
源码和汇编对照阅读 |
disassemble <start>, <end> |
反汇编指定地址区间 |
disassemble <start>, +<len> |
从该地址开始反汇编指定长度 |
6.6 其他实用命令
| 命令 | 说明 |
|---|---|
set print pretty on |
启用结构化缩进显示 |
set print object on |
按照对象的实际派生类型来打印 |
set print elements 0 |
完整打印容器中所有元素 |
6.7 辅助定位工具
| 命令 | 说明 |
|---|---|
addr2line -e <程序> -f -C <地址> |
将符号表地址转换为源代码文件名和行号 |
strings <core文件> | grep -i <关键词> |
从 core 文件中提取可打印字符串,搜索特定关键词 |
nm <程序> |
列出目标文件中的符号表 |
file <文件> |
识别文件类型 |
stat <文件> |
显示文件的详细元数据 |
readelf -h <core文件> |
显示 ELF 文件头信息 |

浙公网安备 33010602011771号