2. GDB 基础入门:安装、启动与核心命令
GDB(GNU Debugger)是 Linux 系统下最经典的符号级调试器,也是缺陷定位工具链的前端核心。本章从零开始搭建 GDB 环境,并掌握调试会话中最常用的 7 个命令,为后续与 Valgrind 联合分析打下基础。
2.1 安装与版本确认
GDB 几乎包含在所有主流发行版的官方源中,安装方式因包管理器而异:
| 发行版 | 安装命令 | 验证命令 |
|---|---|---|
| Ubuntu / Debian | sudo apt install gdb |
gdb --version |
| CentOS / RHEL / Fedora | sudo yum install gdb 或 sudo dnf install gdb |
gdb --version |
| Arch Linux | sudo pacman -S gdb |
gdb --version |
| macOS (Homebrew) | brew install gdb(需额外代码签名) |
gdb --version |
调试符号提示: 使用 GDB 调试前,编译目标程序时必须添加
-g 选项,否则无法查看源码行号和变量名。推荐同时关闭优化 -O0,避免调试时变量被优化掉。
# 编译带调试符号的可执行文件
gcc -g -O0 -o example example.c
2.2 启动 GDB 的三种方式
GDB 提供了灵活的启动入口,适应不同的调试场景:
- 直接加载可执行文件:
gdb ./example—— 最常用方式,启动后进入交互式命令行。 - 附加到正在运行的进程:
gdb -p <PID>—— 用于调试已经启动的服务或卡死进程。 - 分析 core dump 文件:
gdb ./example core—— 事后分析崩溃现场,无需复现。
实战建议: 课程后续章节将大量使用第一种方式。附加进程和 core dump 分析会在“崩溃现场还原”部分深入讲解。
2.3 核心命令速览
以下 7 个命令覆盖了 90% 的日常调试操作,按“启动→设置断点→单步执行→观察变量→退出”的流程组织:
| 命令 | 缩写 | 功能说明 | 常用示例 |
|---|---|---|---|
run |
r |
启动程序,可带命令行参数 | run arg1 arg2 |
break |
b |
设置断点(行号/函数/条件) | b main、b 42、b foo if x > 5 |
next |
n |
单步执行,不进入函数内部 | n 或 n 3(执行 3 步) |
step |
s |
单步执行,进入函数内部 | s |
continue |
c |
恢复执行直到下一个断点 | c |
print |
p |
打印变量或表达式的值 | p x、p &y、p array[3] |
quit |
q |
退出 GDB | q |
2.4 典型调试流程(内联 SVG)
下图展示了一次标准 GDB 调试会话的完整步骤,从启动到退出,箭头表示命令触发的事件流转:
2.5 命令组合实战示例
以下是一个完整的调试会话,演示如何定位一个简单的除零错误:
# 1. 编译带调试符号的程序
gcc -g -O0 -o divide divide.c
# 2. 启动 GDB
gdb ./divide
# 3. 在 main 函数入口设置断点
(gdb) break main
Breakpoint 1 at 0x4004f6: file divide.c, line 8.
# 4. 运行程序
(gdb) run
Starting program: /home/user/divide
Breakpoint 1, main () at divide.c:8
8 int a = 10, b = 0, result;
# 5. 单步执行到除法操作
(gdb) next
9 result = a / b;
# 6. 打印变量确认
(gdb) print a
$1 = 10
(gdb) print b
$2 = 0
# 7. 继续执行,程序崩溃
(gdb) continue
Program received signal SIGFPE, Arithmetic exception.
0x00000000004004fc in main () at divide.c:9
9 result = a / b;
# 8. 退出 GDB
(gdb) quit
常见误区: 初学者容易混淆
next 和 step。当调试到函数调用时,next 会直接执行完整个函数并停在下一行,而 step 会跳入函数内部逐行执行。如果希望观察函数内部逻辑,必须使用 step。
2.6 命令对比速查表
为了帮助快速区分容易混淆的命令对,下表总结了关键差异:
| 命令对 | 行为差异 | 适用场景 |
|---|---|---|
next vs step |
next 不进入函数;step 进入函数 |
确认函数返回值时用 next;排查函数内部缺陷时用 step |
continue vs run |
continue 从暂停点恢复;run 从头启动 |
调试循环或多次触发时用 continue;需要重置状态时用 run |
print vs break 条件 |
print 手动查看;条件断点自动暂停 |
少量变量用 print;大量循环中监控特定值用条件断点 |