3. GDB启动与退出
说实话,很多初学者觉得GDB启动就是敲个gdb回车完事。但我在实际项目中踩过不少坑——比如调试嵌入式交叉编译的程序时,启动参数没给对,直接报错说“格式不对”,排查了半天才发现是目标文件架构不匹配。所以这一节,咱们把GDB的启动和退出彻底聊透。
3.1 启动GDB的三种方式
我个人习惯把启动方式分成三类,每种都有它的适用场景。你想想看,平时写代码时是不是也分“快速调试”和“挂载调试”?
方式一:直接启动并加载可执行文件
这是最常用的方式。直接在命令行里把可执行文件作为参数传给GDB:
gdb ./my_program
GDB启动后,会加载my_program的符号表。这时候你还没运行程序,但已经可以设置断点了。我在项目中经常用这种方式——比如写了一个内存泄漏的demo,先启动GDB,然后从容地设断点、观察变量。
方式二:先启动GDB,再在内部加载文件
有时候你忘了带参数,或者想先看看GDB版本、检查一下环境。那就先启动GDB,进去后再用file命令加载:
gdb
(gdb) file ./my_program
嗯,这里要注意:file命令会重新加载符号表。如果你之前设过断点,加载新文件后断点可能会失效。我曾经犯过这个错——调试到一半换了个二进制文件,结果断点全丢了,还得重新设一遍。
方式三:附加到正在运行的进程
这是我最喜欢的方式之一。程序已经跑起来了,突然发现它卡住了或者内存飙高。这时候不用重启程序,直接附加GDB上去:
gdb -p 12345 # 12345是进程PID
或者先启动GDB,再用attach命令:
gdb
(gdb) attach 12345
为什么说这个好用?有一次线上服务内存泄漏,进程跑了三天才出问题。重启就复现不了,我直接gdb -p挂上去,抓到了那个野指针。说白了,这种方式对排查“偶发问题”特别管用。
detach命令释放,别让服务一直挂着。
3.2 GDB命令行参数
GDB的命令行参数其实挺多的,但日常用的就那几个。我整理了一张表,你一看就明白:
| 参数 | 说明 | 示例 |
|---|---|---|
-q |
安静模式,不打印版本信息和版权声明 | gdb -q ./a.out |
-p |
附加到指定PID的进程 | gdb -p 12345 |
-ex |
启动后立即执行一条GDB命令 | gdb -ex "break main" -ex "run" ./a.out |
-batch |
批处理模式,执行完命令后自动退出 | gdb -batch -ex "run" -ex "bt" ./a.out |
-x |
从文件读取GDB命令并执行 | gdb -x commands.gdb ./a.out |
-cd |
指定工作目录 | gdb -cd /home/user/project ./a.out |
我个人最常用的是-ex和-batch的组合。比如写自动化测试脚本时,一条命令搞定:
gdb -batch -ex "run" -ex "bt" -ex "quit" ./test_program
这样GDB启动后自动运行程序,如果崩溃了自动打印调用栈,然后退出。省得手动敲命令了。
debug.gdb),然后用-x加载。我习惯把断点设置、环境变量都写在脚本里,每次调试直接gdb -x debug.gdb ./a.out,省时省力。
3.3 退出GDB
退出GDB其实就两个命令:quit和q。但这里有个坑——如果你附加到了进程,直接quit会怎样?
答案是:GDB会先detach进程,然后自己退出。被调试的进程会继续运行,不会受影响。但如果你是用run启动的程序,直接quit会终止那个进程。
我曾经犯过一个错:调试一个重要的后台服务,用gdb -p附加上去,查完问题后直接quit了。结果服务还在跑,但GDB退出了,一切正常。后来我专门验证过,GDB在退出时会自动detach,不会杀掉进程。嗯,这点设计得挺人性化。
另外,如果你在GDB里改了内存或者寄存器,退出时这些修改不会自动恢复。所以调试时改过的数据,程序继续跑的时候还是改过的状态。这一点要心里有数。
quit或q:正常退出,附加的进程会被释放Ctrl+D:在GDB命令行下按Ctrl+D,效果等同于quitdetach+quit:先手动分离进程,再退出(更安全)
3.4 知识体系一览
为了让你更直观地理解GDB启动与退出的整体脉络,我画了一张流程图。说白了,启动方式决定了你调试的“入口”,而退出方式决定了你调试的“收尾”。
这张图把启动、参数、退出三个维度串起来了。你想想看,平时调试时是不是经常在“启动方式”和“参数”之间做选择?比如快速调试用gdb ./program,自动化测试用gdb -batch,线上问题用gdb -p。说白了,没有最好的方式,只有最适合场景的方式。
test.gdb文件里,然后用gdb -batch -x test.gdb ./unit_test一键跑完。这样既不用手动敲命令,又能保证每次调试环境一致。你可以试试看。