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 GDB能做什么 安装与启动 GNU调试器 源码级调试 支持C/C++/Go/Rust等 断点管理 单步执行 变量/内存查看 调用栈分析 多线程/反汇编 Linux: apt/yum macOS: brew Windows: MinGW 三种启动方式 编译加 -g 选项 掌握GDB = 调试效率提升10倍

这张图把本章的核心内容串起来了。你看到没?GDB其实就三件事:它是什么、它能干什么、怎么装怎么用。后面的章节我们会一步步深入每个细节。

好了,第一章就到这里。GDB已经装好了,第一个调试会话也跑通了。接下来,你就可以用它来真正解决实际问题了。