跳到主要内容

3.0 准备工作与本章动机

这一章,我们要深入内核的「黑盒」内部。

在用户空间写代码时,printf 是你的左膀右臂;但在内核里,情况变得微妙得多。你面对的不再是一个受保护的进程,而是一个在这个系统中拥有最高权限、却也是最脆弱的执行环境。一次不当的内存访问、一个死锁,甚至一个时机不对的打印语句,都可能导致整个系统瞬间宕机。

这正是许多内核新手(甚至老手)最头疼的地方:当系统崩溃时,你该如何调试它本身?

这就需要一种在没有任何第三方工具依赖的情况下,依然能够工作的调试手段。这种手段必须足够简单,简单到不需要复杂的 UI;同时又必须足够底层,能够穿透各种抽象层直达问题的核心。

在内核世界里,这种手段就是 printk——以及围绕它构建的一整套日志与追踪机制。

但这并不是一个简单的话题。很多人以为 printk 就是内核版的 printf,这种理解是对的,但只对了一半。接下来,我们会看到它如何在极其有限的资源下构建一个环形缓冲区,如何通过日志级别过滤噪音,甚至如何在系统完全挂起之前通过 Early printk 留下最后的话。

准备好你的环境。

我们在这里不需要安装任何新东西。正如第 1 章所述,你的工作环境——不管是虚拟机还是物理板——应该已经就位。代码就在那里,工具链就绪,你的调试之旅将从这一节开始。


3.1 技术要求

如果你翻回去看第 1 章,可能会觉得我在重复自己——但这确实是好事。

这意味着我们的战场没有变,武器库也是熟悉的那些。不需要重新折腾交叉编译器,不需要再去搞那些让你头秃的依赖库。我们可以把精力全部放在核心问题上:怎么看清内核内部正在发生什么

为了确保我们能一起跑完本章的所有实验,这里有一个简短的检查清单:

  1. 开发环境:你依然需要第 1 章里搭建好的 Linux 开发环境(主机通常是 Linux 发行版,目标机可以是 QEMU 虚拟机或 ARM 板子)。

  2. 内核源码:虽然我们会看代码,但你不需要从头手写一个内核。所有的示例代码都打包好了,就在本书的 GitHub 仓库里:

    https://github.com/PacktPublishing/Linux-Kernel-Debugging

    如果你的本地仓库还没更新,现在是 git pull 的好时机。

  3. 必要的权限:在后面的章节里,我们会频繁读取内核日志缓冲区(dmesg)和调试文件系统(debugfs)。这意味着你需要在宿主机上拥有 root 权限,或者配置好相应的 sudo 规则。别在这个地方卡壳——没有什么比敲了一堆命令才发现 Permission denied 更让人血压拉满的了。

环境确认无误之后,我们正式开始。

第一个要面对的问题,也是我们这一章要解决的核心问题是:当 printk 被调用时,那些字符到底去了哪里?

这是一个看似简单,但背后牵扯到内核设计哲学的问题。我们在下一节会把它拆开来看。