Core Dump 文件:配置生成、ulimit 设置、gdb 加载核心文件
调试 crash 最有力的武器是什么?我个人觉得就是 core dump。说白了,程序崩溃那一瞬间的内存快照,里面包含了调用栈、变量值、寄存器状态。拿到它,就能还原事故现场。我曾经在线上追一个偶现的段错误,靠 core dump 十分钟定位,比打 log 重新跑快太多了。这章我们就把它彻底搞明白。
什么是 core dump?
当程序收到 SIGSEGV、SIGABRT 这类致命信号时,操作系统会把进程的内存映像写到磁盘上,这个文件就叫 core dump。默认文件名就是 core,当然也可以配成 core.进程号 之类。你想想看,如果没这个文件,你只能靠 log 脑补现场,多痛苦。
配置生成 core dump
系统有两个关键开关:
- 是否允许生成:由
/proc/sys/kernel/core_uses_pid控制,写 1 表示文件名追加 PID。 - 文件路径和格式:
/proc/sys/kernel/core_pattern控制。默认是core,你可以改成/tmp/core_%e_%p这样更清晰。
核心命令:检查当前配置
cat /proc/sys/kernel/core_pattern
cat /proc/sys/kernel/core_uses_pid
临时修改(重启失效):
echo "/home/core/%e_%p_%t" > /proc/sys/kernel/core_pattern
echo 1 > /proc/sys/kernel/core_uses_pid
我习惯把 core 文件统一放在 /var/crash 目录,按程序名和时间命名,方便集中管理。你最好也开一个独立目录,别让 core 污染项目根路径。
ulimit 设置
光配置了系统层还不够,shell 层还有一个限制:ulimit -c。默认很多服务器是 0,也就是不生成 core。你得先把这个打开。
| ulimit -c 值 | 含义 |
|---|---|
| 0 | 完全不生成 core |
| unlimited | 无大小限制,推荐 |
| 特定数字(如 1048576) | 只允许 core 文件最大 1MB,可能会导致文件被截断 |
记忆小技巧:调试阶段直接 ulimit -c unlimited,生产环境可以设一个上限防止磁盘爆满。但千万别设 0,否则 crash 后你连后悔的机会都没有。
永久设置?修改 /etc/security/limits.conf:
# 针对所有用户,soft 和 hard 都设为无限制
* soft core unlimited
* hard core unlimited
然后重新登录生效。我曾见过有人只在 shell 里设了 ulimit,但服务是通过 systemd 启动的,结果 core 出不来。systemd 服务要单独配置 LimitCore=infinity。
gdb 加载 core 文件
拿到 core 文件后,用 gdb 一把梭:
gdb /path/to/program /path/to/core
进入 gdb 后,第一个命令就是 bt(backtrace),看调用栈:
(gdb) bt
#0 0x00007f... in __memmove_avx_unaligned_erms ()
#1 0x00004... in my_func (p=0x0) at source.c:123
#2 0x00005... in main () at source.c:456
看到 p=0x0 了吧?空指针访问,一眼丁真。你还可以 frame N 切到指定栈帧,用 info locals 看局部变量,用 p variable 打印变量值。
注意:编译时一定要加 -g 选项保留符号。如果没有符号,gdb 只能看到地址和偏移,定位难度大增。我前公司有个历史遗留模块,偏偏没开 -g,每次 core 都得反汇编猜,血的教训。
实战案例:定位一个空指针 crash
假设你有一个程序 server 崩溃了,生成了 core.12345:
- 检查
ulimit -c和core_pattern,确保下次还能生成。 gdb ./server core.12345加载。bt看到 crash 在process_request函数。frame 3切进去,info locals发现request == NULL。- 回看源码,原来是没判空就解引用了。修复完,问题解决。
整个过程不到五分钟。这就是 core dump 的威力。
避坑指南
- 文件权限:core 文件默认 0644,但服务可能以低权限用户运行,导致无法写入。确保目标目录可写。
- 磁盘空间:core 文件可能很大(尤其多线程、大内存服务)。我建议设置
ulimit -c 104857600(100MB),既能定位问题又不会撑爆磁盘。 - 重复覆盖:如果不加 PID 或时间戳,多次 crash 会互相覆盖。设置
core_uses_pid=1或自定义 pattern 带%p、%t。 - 容器环境:Docker 容器默认可能不开启 core dump,需要
--privileged或手动挂载/proc/sys。
我的习惯:在项目根目录写一个 init_core.sh 脚本,每次调试前运行,确保环境一致。省得每次都去配。
最后强调一点:拿到 core 后先别急着格式化,备份一份到存档。同一个 bug 可能不同环境表现不一样,多份 core 对分析干扰因素很有帮助。好了,到这里,core dump 的配置、生成、加载、定位你已经全掌握了。