3、GDB启动与退出:启动GDB的多种方式、退出GDB、GDB的配置文件.gdbinit

调试器怎么启动?这问题听起来简单,但实际工作中我见过不少新手在这上面栽跟头。GDB的启动方式其实有好几种,每种都有它的适用场景。今天我就把这些年积累的经验都抖出来,咱们一起捋一捋。

3.1 启动GDB的多种方式

启动GDB,说白了就是告诉它「嘿,我要调试这个程序」。但具体怎么告诉它,有讲究。

3.1.1 直接启动:gdb 程序名

这是最常用的方式。你编译好了一个程序,比如叫 myapp,直接在终端敲:

gdb myapp

GDB就会加载这个程序,然后停在提示符等你下命令。我个人习惯用这种方式,简单直接。

小提示: 编译时记得加 -g 选项,不然调试信息不全,你看到的全是汇编代码,那滋味可不好受。

3.1.2 带参数启动:gdb --args 程序名 参数1 参数2

如果你的程序需要命令行参数,比如 ./myapp -v --output result.txt,怎么办?

你可能会想:gdb ./myapp -v --output result.txt。错!这样GDB会把 -v 当成自己的参数,程序根本拿不到。

正确做法是:

gdb --args ./myapp -v --output result.txt

或者先启动GDB,再用 set args 命令设置参数:

(gdb) set args -v --output result.txt
(gdb) run

嗯,这里要注意:--args 后面的所有内容都会被当作程序的参数,包括程序名本身。我曾经在这上面吃过亏,调试一个网络服务时传错了参数,排查了半天才发现是启动方式不对。

3.1.3 附加到正在运行的进程:gdb -p PID

这是我最常用的方式之一。程序已经在跑了,突然出问题了,你不能把它停下来重新启动吧?

这时候用 gdb -p 12345(12345是进程ID),GDB就会attach上去,你可以查看当前状态、设置断点、继续运行。

gdb -p 12345

或者先启动GDB,再attach:

(gdb) attach 12345
警告: attach到进程后,GDB会暂停这个进程。如果你在调试生产环境的服务,记得小心操作,别让服务中断太久。我曾经在线上环境attach了一个数据库进程,忘了恢复执行,结果...嗯,那天的晚饭吃得不太愉快。

3.1.4 调试core dump文件:gdb 程序名 core文件

程序崩溃了,生成了一个core dump文件。这时候怎么查?

gdb ./myapp core.12345

GDB会加载core文件,直接定位到程序崩溃的位置。你可以查看当时的堆栈、变量值,就像程序暂停在那里一样。

为什么要有core文件?因为程序崩溃后,现场就没了。core文件相当于一个「快照」,把程序崩溃时的内存状态都保存了下来。我调试过的最棘手的一个bug,就是靠core文件找到的——一个野指针在特定条件下才触发,复现了三天才抓到一次core。

3.1.5 其他启动方式

启动方式 命令 适用场景
调试子进程 gdb --follow-fork-mode child 程序名 程序fork出子进程,你想调试子进程
远程调试 gdb 程序名 然后 target remote 192.168.1.100:1234 程序运行在嵌入式设备或另一台机器上
调试内核模块 gdb vmlinux /proc/kcore 内核调试(需要root权限)

3.2 退出GDB

退出GDB,你可能会说:这还用教?quit 不就完了?

对,但也不全对。GDB退出时有一些细节需要注意。

3.2.1 正常退出

(gdb) quit
# 或者简写
(gdb) q

如果被调试的程序还在运行,GDB会问你是否要终止它:

(gdb) quit
The program is running.  Exit anyway? (y or n) y

选 y,程序会被杀掉。选 n,GDB继续调试。

3.2.2 强制退出

有时候GDB卡住了,比如网络远程调试时连接断了,quit 没反应。这时候可以:

Ctrl + d

或者直接关掉终端窗口。不过我不建议这么做,因为可能会留下一些后台进程。

小技巧: 如果你attach到了进程,退出前记得先 detach,让进程恢复运行。不然进程会被杀掉。

3.3 GDB的配置文件 .gdbinit

每次启动GDB都要设置一堆东西?比如设置断点、加载符号、配置显示风格?太麻烦了。

.gdbinit 就是干这个的。GDB启动时会自动读取这个文件,执行里面的命令。

3.3.1 全局 .gdbinit

放在你的home目录下:~/.gdbinit

所有GDB会话都会加载它。我一般在这里放一些通用配置:

# 设置显示风格
set print pretty on
set print elements 0
set print array-indexes on

# 设置历史记录
set history save on
set history size 1000

# 设置汇编风格为Intel
set disassembly-flavor intel

# 设置断点提示
set breakpoint pending on

3.3.2 项目级 .gdbinit

放在项目目录下:./.gdbinit

GDB默认不会加载当前目录的 .gdbinit,因为安全原因。你需要:

# 在 ~/.gdbinit 中添加
set auto-load safe-path /

或者启动时指定:

gdb -x ./.gdbinit ./myapp

项目级的 .gdbinit 适合放一些项目特定的配置:

# 设置源码路径
directory /home/user/project/src

# 设置断点
break main
break file_io.c:100

# 设置环境变量
set env LD_LIBRARY_PATH ./lib

# 运行参数
set args --config config.json
重要: 我曾经在项目里放了一个 .gdbinit,里面设置了 run 命令。结果每次启动GDB,程序就自动跑起来了,根本来不及设断点。所以 .gdbinit 里尽量不要放 run 或 continue 这类会改变程序执行流程的命令。

3.3.3 禁用 .gdbinit

有时候你不想加载任何配置文件,比如调试别人的项目时,怕被坑。可以用:

gdb -nx ./myapp

-nx 表示不加载任何 .gdbinit 文件。干干净净,从头开始。

3.4 知识体系图

下面这张图把GDB启动与退出的核心知识点串起来了,你可以对照着看:

GDB启动与退出知识体系 GDB启动与退出 启动方式 gdb 程序名 gdb --args 程序名 参数 gdb -p PID gdb 程序名 core文件 退出方式 quit / q Ctrl + d 先detach再quit .gdbinit 配置文件 全局:~/.gdbinit 项目级:./.gdbinit 禁用:gdb -nx 核心:选对启动方式 + 用好配置文件 = 高效调试 启动方式决定你如何进入调试,配置文件决定你调试的起点

这张图把GDB启动与退出的核心脉络画出来了。左边是启动方式,右边是退出方式,中间是配置文件。你想想看,掌握了这三块,GDB的「门」你就摸透了。

我个人建议,把常用的配置放到 ~/.gdbinit 里,项目特定的放到项目目录的 .gdbinit 里。这样每次启动GDB,环境就已经准备好了,省心省力。

避坑指南: 我曾经在调试一个大型C++项目时,发现GDB启动特别慢。排查了半天,原来是 ~/.gdbinit 里加载了一个巨大的符号文件。从那以后,我养成了一个习惯:.gdbinit 里只放最必要的配置,其他的按需加载。

好了,GDB的启动与退出就聊到这儿。记住一句话:启动方式选对了,调试就成功了一半。配置文件用好了,调试效率翻倍。

可可精品资料库