Linux-GDB 调试器入门
GDB(GNU Debugger)是Linux平台下最强大的调试工具,掌握它可以让你从“代码能跑通”进化到“代码为什么能跑通/跑崩”。下
一、GDB是什么?
GDB是GNU开源组织开发的调试器,支持C、C++、Go、Rust等多种语言。它的核心能力可以概括为四句话:
1. 启动程序:按你的要求运行目标程序。
2. 让程序暂停:在指定条件下停止执行。
3. 检查现场:程序暂停时,查看变量、内存、寄存器、调用栈等状态。
4. 修改环境:改变程序中的变量或数据,观察后续行为。
1.1 编译准备:-g选项是关键
想让GDB读懂你的程序,编译时必须加上 -g 选项,这会将符号表(变量名、行号等)保留在可执行文件中。
gcc -g myprogram.c -o myprogram # 生成带调试信息的程序
对比一下,加 -g 编译出的文件通常比不加的大,因为包含了额外的调试信息。
1.2 启动GDB的三种方式
GDB可以灵活地介入程序的各个生命周期:
| 启动方式 | 命令示例 | 适用场景 |
| :--- | :--- | :--- |
| **直接调试** | `gdb ./myprogram` | 从头开始调试程序。 |
| **附加进程** | `gdb -p <PID>` | 调试正在运行的进程(如卡死的服务)。 |
| **分析Core文件** | `gdb ./myprogram core` | 程序崩溃后,复盘事故现场(需先生成core文件)。 |
> 如果不想看GDB启动时的版权声明,可以加上 `-q` 或 `--silent` 参数。
---
二、GDB入门:基础调试命令
假设我们有如下程序 test.c:
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
}
printf("sum = %d\n", sum);
return 0;
}
编译并启动调试:gcc -g test.c -o test 然后 gdb ./test。
核心命令速查表
l(list):显示源代码。默认显示 10 行。一直按回车会继续往下显示。你可以输入l 15来查看第 15 行附近的代码,或者l main查看 main 函数。
b(break):打断点。程序运行到这里会立刻暂停。
b 20:在第 20 行打断点。
b func_name:在进入某个函数时打断点。
info b:查看目前打了哪些断点。
r(run):运行程序。程序会一直跑到你打的第一个断点处停下来。(如果没打断点,程序会直接跑完)。
n(next):单步执行(不进入函数)。也就是常说的 Step Over。执行当前行,如果当前行有函数调用,它会直接把函数执行完,停在下一行。
s(step):单步执行(进入函数)。也就是常说的 Step Into。如果当前行是一个函数,按s会一头扎进这个函数的内部去看个究竟。
c(continue):继续运行。让程序从当前暂停的位置继续狂奔,直到遇到下一个断点。
p(print):打印变量的值。(极其好用!)
p my_var:查看变量my_var当前里面装了什么。
p &my_var:查看这个变量的内存地址。
bt(backtrace):查看函数调用栈。(排查段错误的神器!) 程序崩溃时,直接输入bt,它会告诉你程序是在哪个函数的哪一行死掉的,以及它是怎么一层层被调用过来的。
q(quit):退出 GDB。
操作流程演示:
(gdb) b main # 在main函数入口设断点
(gdb) r # 运行程序,停在main入口
(gdb) l # 查看源代码
(gdb) b 5 if i == 3 # 在第5行设置条件断点(循环到i==3时才触发)
(gdb) c # 继续运行,程序将停在i==3的那次循环
(gdb) p i # 打印i的值,确认是3
(gdb) p sum # 打印sum的值
(gdb) n # 执行下一行(sum += i)
(gdb) quit # 退出
三、断点高级玩法:让调试更精准
面对复杂的循环或多层调用,基础断点会频繁暂停,让人烦躁。高级断点技巧可以让你“指哪打哪”。
3.1 条件断点
只在满足特定条件时才暂停,非常适合调试循环中的特定迭代。
# 语法:break 位置 if 条件
(gdb) break test.c:10 if i == 100 # 循环到第100次时暂停
(gdb) break func if result < 0 # 当func函数中result变为负数时暂停
设置后,可以用 condition 命令修改断点的条件:
(gdb) condition 1 i == 500 # 将编号为1的断点条件改为 i==500
3.2 监视点(Watchpoint)
断点关注“位置”,而监视点关注“数据”。当被监视的变量值发生变化时,程序自动暂停。这是定位“变量被谁意外修改”的神器。
(gdb) watch sum # 当sum的值发生变化时暂停
Hardware watchpoint 2: sum
(gdb) r
... 程序运行 ...
Hardware watchpoint 2: sum
Old value = 3
New value = 6 # GDB会精确告诉你变化前后的值
3.3 断点的管理
临时禁用:
disable 1(禁用编号为1的断点,方便后续恢复)重新启用:
enable 1批量删除:
delete(不加编号,删除所有断点)
四、变量跟踪与调用栈:洞悉程序内部
4.1 持续监视变量(display)
用 print 需要每次手动敲,而 display 可以让GDB在每次暂停时自动打印你关心的变量。
(gdb) display sum
1: sum = 0
(gdb) display i
2: i = 1
(gdb) n
... 每次执行后,都会自动显示 sum 和 i 的值 ...
取消监视:undisplay 1 (取消编号为1的显示项)
4.2 查看调用栈(backtrace)
当程序陷入深层调用或崩溃时,bt 命令可以清晰地展示函数调用链条,让你知道“我是怎么来到这里的”。
(gdb) bt
#0 add (a=3, b=5) at math.c:3
#1 0x400567 in calc () at main.c:8
#2 0x400600 in main () at main.c:15
-
frame 1:切换到#1对应的栈帧(calc函数),查看那层的局部变量。
4.3 查看局部变量
info locals 一次性打印当前函数的所有局部变量,非常高效。
(gdb) info locals
sum = 15
i = 5
五、多线程/多进程调试:并发问题克星
调试多线程程序时,GDB提供了专门的命令。
5.1 线程相关命令
查看所有线程:
info threads切换线程:
thread <线程ID>让指定线程执行命令:
thread apply <线程ID> <命令>,例如thread apply 2 bt查看线程2的调用栈。查看所有线程的栈:
thread apply all bt(崩溃后第一时间执行此命令,非常有价值)锁住调度:
set scheduler-locking on,防止单步调试时切换到其他线程,避免混乱。
5.2 进程调试
对于 fork() 创建的子进程,可以设置GDB跟随哪个进程:
(gdb) set follow-fork-mode child # 调试子进程
(gdb) set follow-fork-mode parent # 调试父进程(默认)
六、事后分析:Core Dump调试
程序崩溃后生成的核心转储文件(core dump)是“案发现场的尸检报告”,能让你在崩溃发生后还原现场。
6.1 生成Core文件
首先确保系统允许生成core文件:
ulimit -c unlimited # 设置core文件大小无限制
然后运行程序直到崩溃,会在当前目录生成 core 或 core.PID 文件。
6.2 分析Core文件
gdb ./myprogram core # 加载程序和core文件
(gdb) bt # 立即查看崩溃时的调用栈,通常就能定位到出错函数
(gdb) info locals # 查看崩溃点的局部变量
(gdb) list # 查看崩溃点附近的源代码
更多推荐




所有评论(0)