Linux程序崩溃调试流程
实验目标
通过手写 “故意崩溃” 的 C 程序,完整复现「程序崩溃 → systemd 生成 core dump → GDB 分析定位崩溃代码行」的全流程,掌握 Linux 系统核心调试方法。
环境说明
- 系统:CentOS/RHEL 等基于 systemd 的 Linux 发行版
- 依赖:
gcc、gdb、lz4(用于解压 systemd 压缩的 core 文件)
第一步:编写 “故意崩溃” 的 C 程序
创建 test_crash.c 文件(故意访问空指针触发段错误 SIGSEGV):
#include <stdio.h>
int main() {
char *null_ptr = NULL; // 故意制造空指针解引用,触发 SIGSEGV 段错误
printf("Crash at this line: %c\n", *null_ptr); // 核心崩溃行:访问空指针指向的内容
return 0;
}
执行以下命令快速创建文件:
cat > test_crash.c << EOF
#include <stdio.h>
int main() {
char *null_ptr = NULL;
printf("Crash at this line: %c\n", *null_ptr);
return 0;
}
EOF
第二步:编译程序(带调试信息)
编译时添加 -g 参数(生成行号、函数名等调试信息,替代系统 debuginfo 包):
# 安装编译依赖(若未安装)
yum install -y gcc gdb
# 编译程序(-g 是调试关键)
gcc -g test_crash.c -o test_crash
-g:强制生成调试信息,确保 GDB 能关联内存地址到具体代码行。
第三步:配置 core dump(适配 systemd 机制)
systemd 会默认接管 core dump 生成(覆盖 kernel.core_pattern 配置),只需确保系统允许生成 core dump 即可:
# 1. 临时开启 core dump(当前 shell 生效,无大小限制)
ulimit -c unlimited
# 2. 验证配置(输出必须为 unlimited,否则无法生成 core)
ulimit -c
说明:systemd 会忽略
kernel.core_pattern配置,core 文件默认生成到/var/lib/systemd/coredump/并以.lz4压缩,无需手动配置路径。
第四步:运行程序,触发崩溃
./test_crash
执行后会立即看到崩溃提示:
Segmentation fault (core dumped)
Segmentation fault:程序触发段错误(非法访问内存);core dumped:systemd 已捕获崩溃并生成 core dump 文件。
可通过系统日志验证崩溃事件:
journalctl -xe | grep test_crash
日志示例(确认 systemd 生成 core):
kernel: test_crash[1768]: segfault at 0 ip 0000000000401136 sp 00007ffc38be79f0 error 4 in test_crash[401000+1000]
systemd-coredump[1770]: Process 1768 (test_crash) of user 0 dumped core.
Subject: Process 1768 (test_crash) dumped core
Process 1768 (test_crash) crashed and dumped core.
第五步:找到 systemd 生成的 core dump 文件
systemd 会将 core 文件压缩为 .lz4 格式,默认存储在 /var/lib/systemd/coredump/,执行以下命令定位:
# 列出 test_crash 相关的 core 文件(通配符匹配,无需手动输完整名)
ls -l /var/lib/systemd/coredump/core.test_crash.*.lz4
输出示例:
-rw-r----- 1 root root 25923 Dec 9 18:15 /var/lib/systemd/coredump/core.test_crash.0.4414d6d27e3e47e2a2194386ecd7a663.1768.1765275338000000.lz4
文件名规则:
core.<程序名>.<用户ID>.<唯一标识>.<PID>.<时间戳>.lz4
第六步:解压 core 文件(适配 GDB 兼容)
大部分老版本 GDB 不支持直接读取 .lz4 压缩的 core 文件,需先解压:
# 安装 lz4 解压工具(若未安装)
yum install -y lz4
# 解压 core 文件到 /tmp(通用命令,自动匹配最新生成的 core 文件)
CORE_FILE=$(ls -t /var/lib/systemd/coredump/core.test_crash.*.lz4 | head -1)
lz4 -d ${CORE_FILE} /tmp/test_crash.core
ls -t:按时间排序,取最新生成的 core 文件(避免匹配多个文件);lz4 -d:解压.lz4压缩文件到/tmp/test_crash.core(路径可自定义)。
第七步:用 GDB 分析 core 文件,定位崩溃代码行
# 加载 编译后的程序 + 解压后的 core 文件
gdb ./test_crash /tmp/test_crash.core
进入 GDB 交互界面后,执行以下 3 个核心命令,精准定位崩溃根因:
1. 查看崩溃调用栈(核心命令)
(gdb) bt
输出示例(直接指向崩溃的函数和代码行):
#0 0x0000000000401136 in main () at test_crash.c:4
- 解读:崩溃发生在
test_crash.c文件第 4 行的main函数中。
2. 查看崩溃位置的源代码
(gdb) list
输出示例(清晰显示崩溃代码行):
1 #include <stdio.h>
2 int main() {
3 char *null_ptr = NULL;
4 printf("Crash at this line: %c\n", *null_ptr);
5 return 0;
6 }
- 解读:第 4 行(实际代码行号可能因格式略有差异)的
*null_ptr是崩溃根源(解引用空指针NULL)。
3. 验证崩溃原因(查看变量值)
(gdb) print null_ptr
输出示例(确认变量为空指针):
$1 = 0x0
- 解读:
null_ptr的值为0x0(即NULL),访问其指向的内容必然触发段错误。
关键补充说明
1. 关于 GDB 提示 “Missing separate debuginfos”
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.34-167.1.el8.x86_64
- 该提示仅表示缺少
glibc库的调试信息(printf依赖 glibc),不影响定位自定义代码的崩溃行; - 若需消除提示,执行
dnf debuginfo-install glibc-<版本号>.x86_64即可(非必需)。
2. 可选:让 systemd 生成未压缩的 core 文件
若不想每次解压,可修改 systemd-coredump 配置关闭压缩:
# 编辑 core dump 配置文件
vi /etc/systemd/coredump.conf
修改以下配置项(去掉注释并调整值):
[Coredump]
Compress=none # 关闭 core 文件压缩
Storage=external # 存储到外部目录(/var/lib/systemd/coredump)
ExternalSizeMax=10G # 限制 core 文件总大小
重启配置生效:
systemctl daemon-reload
systemctl restart systemd-coredump.service
3. 关于 RPM/YUM 安装程序的崩溃调试
手动编译程序可通过-g直接嵌入调试信息,但 YUM/RPM 安装的系统程序(如 nginx、httpd、mysql)默认无调试符号,直接用 GDB 分析仅能看到内存地址,无法定位具体代码行。需通过以下步骤补充调试信息:
- 启用 debuginfo/debugsource 源,安装源配置并开启调试源;
- 安装匹配版本的 debuginfo/debugsource 包,版本严格匹配,版本需与主程序完全一致;
- 验证加载:安装后 GDB 会自动识别并加载调试符号,执行
bt即可解析出函数名和代码行(无需手动指定符号路径)。
4. 核心命令总结
| GDB 命令 | 作用 |
|---|---|
bt |
查看崩溃调用栈(定位崩溃函数 / 代码行) |
list |
显示崩溃位置的源代码上下文 |
print |
查看变量值(验证崩溃根因) |
frame |
切换调用栈帧(复杂程序调试用) |

浙公网安备 33010602011771号