1、GDB入门:什么是GDB、GDB能做什么、GDB的安装与启动
1.1 什么是GDB?一个老牌调试器的自白
GDB,全称GNU Debugger。说白了,它就是程序员的“显微镜”和“手术刀”。
我刚开始做嵌入式开发那会儿,调试代码全靠printf大法。代码里到处塞打印语句,编译、下载、看串口输出,改完再重来一遍。效率低不说,还经常因为忘记删打印语句导致程序跑飞。后来一位老同事看我实在痛苦,扔过来一句:“你怎么不用GDB?”
嗯,从那以后,我的调试生涯才算真正开始。
GDB是一个命令行调试工具。它让你能“看”到程序运行时内部的一切:变量值是多少、函数调用栈长什么样、程序卡在哪一行、内存里到底存了什么。它不挑语言,C、C++、汇编、Go、Rust都能调试。它也不挑平台,Linux、Windows、macOS、甚至裸机嵌入式系统,都能跑。
你想想看,一个工具能让你在程序运行时随时暂停、逐行执行、查看任意变量、修改内存数据——这不比printf香多了?
一句话总结:GDB是GNU项目出品的源码级调试器,是Linux/嵌入式开发者的必备技能。
1.2 GDB能做什么?——我列个清单给你看
很多新手觉得GDB就是“加断点、看变量”。其实远不止这些。我根据自己多年的使用经验,把GDB的核心能力归纳为以下几类:
| 能力分类 | 具体功能 | 我常用的场景 |
|---|---|---|
| 断点管理 | 设置/删除/禁用断点、条件断点、硬件断点 | 定位段错误、死循环、逻辑错误 |
| 单步执行 | 逐行执行、逐函数执行、跳出函数 | 跟踪变量变化、理解代码执行路径 |
| 变量查看 | 打印变量值、查看数组/结构体、监视变量变化 | 检查指针是否越界、结构体成员是否正确 |
| 调用栈分析 | 查看函数调用链、切换栈帧 | 定位崩溃位置、分析递归深度 |
| 内存操作 | 查看/修改内存、查看寄存器 | 检查堆栈溢出、验证内存布局 |
| 多线程/多进程 | 切换线程、查看线程状态、调试子进程 | 排查死锁、竞态条件 |
| 反汇编 | 查看汇编代码、混合源码/汇编显示 | 分析编译器优化、调试启动代码 |
我在项目中遇到过最典型的一个场景:程序运行到某个函数就莫名其妙地崩溃,printf打了一堆也没找到原因。用GDB加个断点,单步执行到崩溃前一行,打印指针地址——发现是个野指针。前后不到5分钟。要是用printf,估计得折腾半天。
我的个人习惯:遇到段错误,第一反应不是加printf,而是用GDB的bt命令看调用栈。十有八九能直接定位问题。
1.3 GDB的安装——不同平台怎么搞?
安装GDB其实很简单。不同操作系统的方法略有不同,我整理了一下:
Linux(Ubuntu/Debian)
sudo apt update
sudo apt install gdb
Linux(CentOS/RHEL/Fedora)
sudo yum install gdb
# 或者
sudo dnf install gdb
macOS
brew install gdb
注意:macOS上安装后还需要额外签名,否则调试时会报权限错误。具体步骤可以查一下官方文档。
Windows
Windows上我建议用MinGW或Cygwin。我个人更推荐MinGW,轻量且够用。
# 安装MinGW后,在MinGW安装目录的bin文件夹下就有gdb.exe
# 或者直接下载TDM-GCC,自带GDB
避坑指南:我曾经在Windows上用Cygwin装GDB,结果路径问题折腾了一下午。后来换成MinGW,5分钟搞定。如果你只是调试C/C++程序,MinGW足够了。
验证安装
装完之后,打开终端输入:
gdb --version
如果看到版本号输出,说明安装成功。比如我现在的版本是:
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
1.4 启动GDB——三种方式,你选哪种?
GDB的启动方式有三种,我分别说说:
方式一:直接启动,再加载程序
gdb
(gdb) file ./myprogram
(gdb) run
这种方式适合你还没想好要调试哪个程序,或者需要先设置一些参数。
方式二:启动时直接加载程序
gdb ./myprogram
这是我最常用的方式。一步到位,直接进入调试界面。
方式三:调试core dump文件
gdb ./myprogram core.12345
程序崩溃后生成的core文件,用这种方式可以直接定位到崩溃位置。我在服务器上排查线上崩溃问题时,这招屡试不爽。
重要提醒:要生成core文件,需要先执行ulimit -c unlimited。否则程序崩溃了也不会生成core文件,你就只能干瞪眼了。
1.5 第一个GDB调试会话——跑起来看看
光说不练假把式。我们来写个简单的程序,实际跑一遍GDB。
先准备一个测试程序 test.c:
#include <stdio.h>
int add(int a, int b) {
int result = a + b;
return result;
}
int main() {
int x = 10;
int y = 20;
int sum = add(x, y);
printf("sum = %d\n", sum);
return 0;
}
编译时记得加 -g 选项,这样才能保留调试信息:
gcc -g -o test test.c
然后启动GDB:
gdb ./test
你会看到GDB的提示符 (gdb)。接下来输入:
(gdb) break main
Breakpoint 1 at 0x11a9: file test.c, line 9.
(gdb) run
Starting program: /home/user/test
Breakpoint 1, main () at test.c:9
9 int x = 10;
看到了吗?程序停在了main函数的第一行。这时候你可以:
next(或n)——执行下一行print x(或p x)——查看变量x的值step(或s)——进入函数内部continue(或c)——继续执行到下一个断点quit(或q)——退出GDB
试试看:
(gdb) n
10 int y = 20;
(gdb) p x
$1 = 10
(gdb) n
11 int sum = add(x, y);
(gdb) s
add (a=10, b=20) at test.c:3
3 int result = a + b;
嗯,这里要注意:用s进入了add函数内部。如果你想直接跳过函数调用,用n就行。
我的小技巧:刚开始用GDB时,记不住命令很正常。可以用help命令查看帮助,或者用help <命令名>查看具体命令的用法。比如help break。
1.6 本章知识体系一览
为了让你对本章内容有个整体认识,我画了一张图:
这张图把本章的核心内容串起来了。你看到没?GDB其实就三件事:它是什么、它能干什么、怎么装怎么用。后面的章节我们会一步步深入每个细节。
好了,第一章就到这里。GDB已经装好了,第一个调试会话也跑通了。接下来,你就可以用它来真正解决实际问题了。