第一章:调试环境搭建

调试环境搭建,说白了就是给你的内核开发配一套趁手的兵器。我见过太多人上来就写驱动,出了问题两眼一抹黑,连个打印都加不进去。嗯,今天我们就把这套环境彻底搞定。

1.1 Linux内核调试环境配置

先说说内核编译。你想想看,调试内核和调试普通程序完全是两码事。普通程序崩了,gdb上去就能看。内核崩了?那叫panic,系统直接挂了。

我个人习惯用Ubuntu 22.04 LTS,内核版本选5.15 LTS。为什么?因为LTS版本稳定,社区支持好,踩过的坑网上都有答案。

核心配置项:

  • CONFIG_DEBUG_INFO=y — 生成调试符号
  • CONFIG_GDB_SCRIPTS=y — 加载内核gdb脚本
  • CONFIG_KGDB=y — 开启KGDB支持
  • CONFIG_KGDB_SERIAL_CONSOLE=y — 串口KGDB
  • CONFIG_FRAME_POINTER=y — 栈回溯支持
# 我的常用编译配置
make menuconfig
# Kernel hacking → Compile-time checks → 
#   Compile the kernel with debug info
# Kernel hacking → KGDB → 
#   KGDB: kernel debugger

make -j$(nproc)
make modules_install
make install

我在项目中遇到过一个问题:编译时忘了开CONFIG_DEBUG_INFO,结果内核崩溃了连个符号都看不到。那叫一个痛苦,全靠printk一行行猜。后来我写了个脚本,每次编译前自动检查这些配置项。

我的小技巧:把常用配置写进kernel/configs/debug.config,然后make defconfig debug.config一键搞定。

1.2 QEMU虚拟机搭建

QEMU是我最推荐的调试平台。为什么?因为它不会搞坏你的物理机。我曾经在真机上调试一个PCIe驱动,一个写操作把整个总线搞挂了,机器直接变砖。从那以后,我所有的新驱动都在QEMU上先跑通。

# 安装QEMU
apt install qemu-system-x86 qemu-utils

# 创建磁盘镜像
qemu-img create -f qcow2 disk.qcow2 10G

# 启动虚拟机(调试模式)
qemu-system-x86_64 \
  -kernel bzImage \
  -initrd initrd.img \
  -append "console=ttyS0 nokaslr" \
  -serial stdio \
  -s -S  # -s: gdb端口1234, -S: 暂停等待调试器

这里有个关键点:nokaslr参数。KASLR(内核地址空间布局随机化)会让内核每次启动地址都不一样,调试器根本没法设断点。我第一次用KGDB时就被这个坑了,断点设了根本触发不了。

注意:生产环境一定要开KASLR,这是安全机制。调试环境关掉它,调试完记得改回来。

1.3 KGDB双机调试

KGDB双机调试,说白了就是一台机器跑内核,另一台机器用gdb连上去调试。我习惯用串口连接,简单可靠。

目标机配置:

# 内核启动参数添加
kgdboc=ttyS0,115200 kgdbwait

# 进入KGDB
echo g > /proc/sysrq-trigger

调试机操作:

# 启动gdb
gdb vmlinux

# 连接目标机
(gdb) target remote /dev/ttyUSB0

# 设置断点
(gdb) b do_fork
(gdb) c

我曾经调试一个调度器bug,用KGDB在do_fork处设断点,观察进程创建流程。嗯,这个场景用printk根本做不到,因为printk本身就可能触发调度。

经验之谈:KGDB调试时,建议用两台独立的机器。虚拟机嵌套也可以,但串口模拟容易出问题。我踩过这个坑,虚拟串口丢数据导致gdb连接不稳定。

1.4 JTAG调试器入门

JTAG调试器,这是硬件调试的终极武器。它不依赖操作系统,可以直接访问CPU寄存器、内存、外设。说白了,就是硬件级别的gdb。

常用的JTAG调试器:

调试器型号 适用平台 价格区间 我的评价
J-Link ARM ¥500-5000 稳定可靠,首选
OpenOCD + FT2232 多种架构 ¥50-200 开源方案,性价比高
Lauterbach 全平台 ¥50000+ 专业级,功能强大

OpenOCD配置示例:

# openocd.cfg
source [find interface/ftdi/ft2232.cfg]
source [find target/stm32f4x.cfg]

# 启动OpenOCD
openocd -f openocd.cfg

# 另一个终端连接gdb
arm-none-eabi-gdb vmlinux
(gdb) target remote localhost:3333
(gdb) monitor reset halt
(gdb) load vmlinux
(gdb) continue

我记得第一次用JTAG调试内核启动流程,那种感觉就像开了上帝视角。你能看到CPU从reset vector开始,一步步执行到start_kernel。每个寄存器的变化都尽收眼底。

硬件警告:JTAG连接时,一定要确认电压匹配。3.3V的调试器接到1.8V的芯片上,可能直接烧掉。我同事就干过这事,一块开发板就这么废了。

知识体系总览

下面这张图是我整理的调试环境搭建知识体系,你可以看到各个组件之间的关系:

Linux内核调试环境知识体系 内核调试环境 内核编译配置 CONFIG_DEBUG_INFO CONFIG_KGDB CONFIG_FRAME_POINTER QEMU虚拟机 -kernel bzImage -s -S (gdb端口) nokaslr参数 KGDB双机调试 kgdboc=ttyS0 target remote 断点设置 JTAG调试器 OpenOCD配置 硬件断点 寄存器访问 从软件到硬件,层层递进,覆盖所有调试场景

调试环境搭建,说白了就是给自己铺路。路铺好了,后面调试起来才能得心应手。我个人建议:先从QEMU开始,熟悉了再上KGDB,最后才是JTAG。一步一个脚印,别想一口吃成胖子。

本章核心要点:

  • 内核调试必须开启DEBUG_INFO和KGDB相关配置
  • QEMU是最安全的调试平台,适合新手入门
  • KGDB双机调试适合复杂场景,注意串口稳定性
  • JTAG是终极武器,但成本高、操作复杂
  • 调试环境搭建是基本功,值得花时间彻底搞定

我的建议:把调试环境配置写成脚本,一键部署。这样换机器、重装系统时,几分钟就能恢复工作环境。我自己的脚本已经迭代了十几个版本,现在换到任何机器上都能秒级进入调试状态。


可可精品资料库