跳到主要内容

第 2 章 黑暗森林的火把

本章建立的核心认知是:调试内核和调试用户态程序完全是两种物种。在用户态你拥有整个 libc 库、隔离的进程空间和随时可以 attach 的 GDB;到了内核态,你的一个指针错误就能让整个机器瘫痪,而且一旦停机,所有的上下文——除了那一堆十六进制数——都会随断电而烟消云散。

理解了这一点之后,内核调试工具链那种「笨重」「复杂」甚至「原始」的设计就不再令人费解了——它本质上是带着镣铐跳舞,必须在极度受限的环境中榨取信息。

环境准备

别急着写代码,先检查装备。

这听起来像是废话,但请相信我,环境没配好就往内核代码里冲,是效率最低的折腾方式。如果你在第一章已经按照「Setting up the workspace」一节搭好了环境,那现在你可以直接复用那一套;如果没有,现在是个好机会回头去补上——我们需要一个稳定的 Linux 开发环境(推荐主机或虚拟机)、交叉编译器链、以及一份能跑通的内核源码。

这里不重复那些琐碎的 apt install 列表了,但你必须确保手头有以下两样东西随时待命:

  1. 构建工具链:能编译内核和模块的 gcc/make 工具集。
  2. 目标系统:一个可以被折腾的 Linux 系统(可以是 QEMU 虚拟机,也可以是一块板子),你能够通过串口或网络看到它的输出,甚至能让它在崩溃时停下来等你检查。

别跳过这一步。等到后面你看着满屏毫无意义的寄存器发呆时,会感激现在多花的那十分钟。


2.1 技术需求与技术栈

硬件与软件前提

和上一章一样,我们要在这里统一一下「频道」。

不管是本地的虚拟机还是远程的开发板,接下来的所有演示都假设你拥有以下控制权:

  • ** root 权限**:内核调试不跟你讲礼貌,加载模块、查看内核内存 (/dev/mem)、配置调试接口,这些都需要 root。
  • 内核源码树:你手里得有一份和目标机器运行版本完全一致的源码(或者至少版本号对得上)。用不匹配的源码去分析崩溃堆栈,就像拿着英文地图去找中文路牌——大概能对上,但关键时刻一定迷路。

工作空间检查清单

让我们快速过一遍工作空间的清单。

不需要你重新打字,但请在脑子里点个卯:

  • 编译器arm-linux-gnueabihf-gccx86_64-linux-gnu-gcc 在你的 $PATH 里吗?
  • 内核配置.config 文件里是不是打开了 CONFIG_DEBUG_KERNELCONFIG_KGDB?(如果这些词看着眼生,别慌,我们马上会细讲)。
  • 串口/网络:你能用 minicompicocom 或者 ssh 连上目标机器吗?

这里千万别偷懒

如果你在上一章搭建环境时跳过了某些步骤,觉得「暂时用不上」,现在就是「算账」的时候。很多调试工具——特别是后面要讲到的 KGDB 和 kdump——对内核版本和配置极度敏感。差一个补丁版本,或者少了一个 .config 选项,你可能会遇到完全没有任何输出,或者更糟:输出一堆误导性的乱码。

先别急着敲代码。

如果这些都 OK,那我们就正式进入内核调试的丛林——先要知道我们要打的是什么样的野兽。