Linux程序崩溃调试流程

实验目标

通过手写 “故意崩溃” 的 C 程序,完整复现「程序崩溃 → systemd 生成 core dump → GDB 分析定位崩溃代码行」的全流程,掌握 Linux 系统核心调试方法。

环境说明

  • 系统:CentOS/RHEL 等基于 systemd 的 Linux 发行版
  • 依赖:gccgdblz4(用于解压 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 分析仅能看到内存地址,无法定位具体代码行。需通过以下步骤补充调试信息:

  1. 启用 debuginfo/debugsource 源,安装源配置并开启调试源;
  2. 安装匹配版本的 debuginfo/debugsource 包,版本严格匹配,版本需与主程序完全一致;
  3. 验证加载:安装后 GDB 会自动识别并加载调试符号,执行bt即可解析出函数名和代码行(无需手动指定符号路径)。

4. 核心命令总结

GDB 命令 作用
bt 查看崩溃调用栈(定位崩溃函数 / 代码行)
list 显示崩溃位置的源代码上下文
print 查看变量值(验证崩溃根因)
frame 切换调用栈帧(复杂程序调试用)
posted @ 2025-12-10 10:50  wanghongwei-dev  阅读(22)  评论(0)    收藏  举报