坚持用 清晰易懂的图解 + 代码语言,让每个知识点变得简单!
🚀呆头个人主页详情
🌱 呆头个人Gitee代码仓库
📌 呆头详细专栏系列
座右铭: “不患无位,患所以立。”
《"告别Bug!GDB/CGDB调试实战指南"》
前言
🚀 欢迎来到《Linux系统实战》!
这里是命令行到内核的跃迁基地,也是你从"rm -rf恐惧症"到"权限管理大师"的修炼场。
🔍 专栏特色:
- 图解+实战:用最直观的方式拆解Linux核心机制
- 从应用到底层:覆盖Shell脚本、系统调优、内核模块开发
- 真实场景:每篇附服务器运维/开发中的实际问题解决方案
💡 学习建议:
1️⃣ 先动手尝试(搞崩了也没关系)
2️⃣ 对照文章分析原理
3️⃣ 用文末【实战任务】巩固技能
📌 Linux经典名言:
“Linux不是背出来的,是在一次次Permission denied
中练出来的!”
(正文开始👇)
目录
一、debug和release
程序的发布方式有两种, debug 模式和 release 模式。
- 在linux中gcc/g++ 出来的⼆进制程序,编译器默认生成的可执行程序(二进制程序)是release发布版本,即追求性能的版本
- 如果想要生成debug版本的可执行程序,就需要我们在使用gcc/g++工具将源文件进行编译链接生成二进制程序的时候进行手动指定选项-g添加debug信息,使我们的可执行程序可以被执行追踪
- 通常debug版本由于添加了debug调试信息,通常都是比默认生成的release版本的所占用的空间大
- gcc 要进行编译的文件 -o 可执行文件名 -g
我们可以发现动态链接的debug版本占用空间略大于release,静态链接也是如此
readelf -S 可执行文件名
可执行文件形成的时候不是无序的,而是有独特的格式,可执行程序有自己的二进制格式(elf格式),我们可以使用readelf -S 可执行文件名,来进行查看可执行文件的二进制格式
同时我们还可以借助管道,使用grep对二进制格式中的信息进行搜索,其中-i选项是忽略大小写进行查找,由于test是release是发行版本,没有添加debug信息,所以无法查找到debug,test_debug是debug版本,添加了debug信息,所以可以查找到debug
二、环境准备
- 编写要进行调试的代码,创建一个test.c的文件用于编写如下代码,这里编写了一个求x减到1的总和函数用于进行我们的调试
#include <stdio.h>
int sum(int s, int e) {
int result = 0;
for(int i = e; i >= s; i--) {
result += i;
}
return result;
}
int main() {
int start = 1, end = 100;
printf("I will begin!\n");
int n = sum(start, end);
printf("running done, result is: [%d~%d]=%d\n", start, end, n);
return 0;
}
- 创建自动化构建工具的文件makefile,并写入指令,这里特别注意由于我们想要生成的是可调试的debug版本的可执行程序,那么要在我们使用gcc命令后面加入-g选项,底行模式wq保存退出之后,在命令行运行make指令生成debug版本的二进制程序test
test:test.c
gcc test.c -o test -g
clean:
rm -f test
- 检查,当你的程序使用readelf -S 文件名 | grep debug查找到如下debug信息代表你生成dubug版本的二进制程序成功,该程序可以使用gdb进行调试
三、gdb/cgdb的使用
🛸GDB 与 CGDB 对比
特性 | GDB(命令行调试器) | CGDB(增强版GDB) |
---|---|---|
用户界面 | 纯命令行交互 | 分屏界面(代码窗口 + GDB 终端) |
代码显示 | 需手动 list 查看代码 |
自动高亮显示源代码 |
调试体验 | 需手动输入命令(如 next 、print ) |
支持快捷键操作(类似 Vim,如 F2 切换窗口) |
代码导航 | 需用 break 或 list 跳转 |
可直接在代码窗口浏览和设置断点(空格键) |
适用场景 | 适合简单调试或远程调试(如服务器环境) | 适合本地开发,提高调试效率(直观的代码导航) |
🛸GDB 调试命令速查表
命令/缩写 | 作用 | 样例 |
---|---|---|
list / l |
显示源代码(从上次位置开始,每次10行) | list / l 10 |
list / l 函数名 |
列出指定函数的源代码 | list main |
list / l 文件名:行号 |
列出指定文件的源代码 | list mycmd.c:1 |
run / r |
从程序开始连续执行 | run |
next / n |
单步执行(不进入函数) | next |
step / s |
单步执行(进入函数) | step |
break / b [文件名:]行号 |
在指定行号设置断点 | break 10 / break test.c:10 |
break / b 函数名 |
在函数开头设置断点 | break main |
info break / info b |
查看所有断点信息 | info break |
finish |
执行到当前函数返回 | finish |
print / p 表达式 |
打印表达式的值 | print start+end |
print / p 变量 |
打印变量的值 | p x |
set var 变量=值 |
修改变量的值 | set var i=10 |
continue / c |
继续执行程序 | continue |
delete breakpoints / d breakpoints |
删除所有断点 | delete breakpoints |
delete breakpoints n / d breakpoints n |
删除指定序号断点 | delete breakpoints 1 |
disable breakpoints |
禁用所有断点 | disable breakpoints |
enable breakpoints |
启用所有断点 | enable breakpoints |
info breakpoints / i breakpoints |
查看断点列表 | info breakpoints |
display 变量名 |
跟踪显示变量(每次停止时) | display x |
undisplay 编号 |
取消跟踪显示 | undisplay 1 |
until 行号 |
执行到指定行号 | until 20 |
backtrace / bt |
查看函数调用栈及参数 | backtrace |
info locals / i locals |
查看当前栈帧的局部变量 | info locals |
quit |
退出GDB | quit |
以下是整理成 CSDN Markdown 表格格式 的 CGDB 调试命令速查表,包含 基础 GDB 命令 和 CGDB 专属快捷键,方便快速查阅:
🛸CGDB 调试命令速查表
分类 | 命令/快捷键 | 作用 | 示例/说明 |
---|---|---|---|
通用指令 | break / b |
设置断点 | b main (在 main 函数断点) |
run / r |
启动程序 | run |
|
next / n |
单步执行(不进入函数) | next |
|
step / s |
单步执行(进入函数) | step |
|
print / p |
打印变量值 | p x |
|
continue / c |
继续运行程序 | continue |
|
quit / q |
退出调试器 | quit |
|
CGDB 快捷键 | Esc |
从命令窗口切换到代码窗口 | 按 Esc 后可用方向键浏览代码 |
i |
从代码窗口切换到命令窗口 | 按 i 后输入 GDB 命令 |
|
空格 |
在代码窗口设置/取消断点 | 光标移到目标行按 空格 |
|
/ + 文本 |
在代码窗口搜索文本 | /sum (搜索 sum 函数) |
|
: + 行号 |
跳转到指定行 | :20 (跳转到第 20 行) |
|
F2 |
切换代码窗口和命令窗口 | 快速切换焦点 | |
F6 |
在代码窗口快速设置断点 | 等效于 空格 |
|
辅助功能 | info break / i b |
查看所有断点 | info break |
delete / d |
删除断点 | d 1 (删除断点编号 1) |
|
display |
持续显示变量值 | display x (每次暂停时打印 x ) |
🛸启动gdb
sudo yum install -y gdb #gdb下载
sudo yum install -y epel-release # 启用EPEL仓库
sudo yum install -y cgdb # 安装CGDB
gdb 可执行文件名,观察到如下场景即为我们的gdb调试器启动成功,那么接下来我们将在(gdb)命令行中输入调试命令进行我们程序的调试。
q,退出gdb
🛸查看行号对应x行代码
- l n,n代表你所要查看行号,即可显示该行号对应代码,如果输入的行号为1,那么会从第1行开始显示共计10行代码,如果该行号为一个中间的行号,那么会居中显示该行号,并且显示该行号上5行,下5行,共计10行代码
- gdb会默认记住命令,如果你输入命令后,下一次输入默认继续执行上一次的命令,并且如果你查看的是行号会继续从上一次查看的最后一行的位置继续向下查看
🛸运行程序
r ,可以运行程序,如果没有设置断点,那么默认执行到程序的最后,如果设置了断点,那么默认运行到第一个断点的位置
🛸查看函数代码
l 函数名
🛸断点
- b n,n代表行号,在某一行设置断点
- b 函数名,在函数的开头设置断点
- b 源文件: n,在源文件行号为n的位置处设置断点
🍑查看断点
如果你想查询断点信息 info b
🍑删除断点
d n,这个n代表断点的序号,例如下图中的Nnm那一行代表的序号
d breakpoints,可以删除所有断点
🍑禁用,启用断点
- disable n,这个n代表断点的序号,禁用断点
- enable n,这个n代表断点的序号,启动断点
🛸调试
- 这里的调试过程一定是r先运行起来才能进行逐过程调试或逐语句调试
- n,逐过程调试,类似于vs中的F10,不会进入函数
- s,逐语句调试,类似于vs中的F11,会进入函数
🍑逐过程 、 语句调试
🛸打印值
p 变量,调试运行起来,可以进行打印变量对应的值(这里的作用仅仅是打印,无其它作用)
🛸跟踪、取消变量
display 变量名,跟踪查看一个变量,每次停下来都显示它的值,类似于vs中的监视窗口监视变量的作用
undisplay,可以取消对所有变量的追踪(这里不可以单独取消对某个变量的追踪,要取消只能统一取消对全部变量的追踪)
🛸设置局部变量值
set var 变量名=你要给变量设置的值,比如循环前50次没有问题,你想快速跳转至第51次有没有问题,可以使用修改循环变量的值达到快速跳转进行调试
🛸跳到下一个断点
c,跳转到下一个断点
🛸跳到指定行
until n,n代表行号,跳转到指向行n
🛸函数调用堆栈
bt,查看函数调用堆栈(即各级函数调用及其参数)