3.0 准备工作与本章动机
这一章,我们要深入内核的「黑盒」内部。
在用户空间写代码时,printf 是你的左膀右臂;但在内核里,情况变得微妙得多。你面对的不再是一个受保护的进程,而是一个在这个系统中拥有最高权限、却也是最脆弱的执行环境。一次不当的内存访问、一个死锁,甚至一个时机不对的打印语句,都可能导致整个系统瞬间宕机。
这正是许多内核新手(甚至老手)最头疼的地方:当系统崩溃时,你该如何调试它本身?
这就需要一种在没有任何第三方工具依赖的情况下,依然能够工作的调试手段。这种手段必须足够简单,简单到不需要复杂的 UI;同时又必须足够底层,能够穿透各种抽象层直达问题的核心。
在内核世界里,这种手段就是 printk——以及围绕它构建的一整套日志与追踪机制。
但这并不是一个简单的话题。很多人以为 printk 就是内核版的 printf,这种理解是对的,但只对了一半。接下来,我们会看到它如何在极其有限的资源下构建一个环形缓冲区,如何通过日志级别过滤噪音,甚至如何在系统完全挂起之前通过 Early printk 留下最后的话。
准备好你的环境。
我们在这里不需要安装任何新东西。正如第 1 章所述,你的工作环境——不管是虚拟机还是物理板——应该已经就位。代码就在那里,工具链就绪,你的调试之旅将从这一节开始。
3.1 技术要求
如果你翻回去看第 1 章,可能会觉得我在重复自己——但这确实是好事。
这意味着我们的战场没有变,武器库也是熟悉的那些。不需要重新折腾交叉编译器,不需要再去搞那些让你头秃的依赖库。我们可以把精力全部放在核心问题上:怎么看清内核内部正在发生什么。
为了确保我们能一起跑完本章的所有实验,这里有一个简短的检查清单:
-
开发环境:你依然需要第 1 章里搭建好的 Linux 开发环境(主机通常是 Linux 发行版,目标机可以是 QEMU 虚拟机或 ARM 板子)。
-
内核源码:虽然我们会看代码,但你不需要从头手写一个内核。所有的示例代码都打包好了,就在本书的 GitHub 仓库里:
https://github.com/PacktPublishing/Linux-Kernel-Debugging如果你的本地仓库还没更新,现在是
git pull的好时机。 -
必要的权限:在后面的章节里,我们会频繁读取内核日志缓冲区(
dmesg)和调试文件系统(debugfs)。这意味着你需要在宿主机上拥有root权限,或者配置好相应的sudo规则。别在这个地方卡壳——没有什么比敲了一堆命令才发现Permission denied更让人血压拉满的了。
环境确认无误之后,我们正式开始。
第一个要面对的问题,也是我们这一章要解决的核心问题是:当 printk 被调用时,那些字符到底去了哪里?
这是一个看似简单,但背后牵扯到内核设计哲学的问题。我们在下一节会把它拆开来看。