跳到主要内容

第 7 章 当内核开始咆哮

有一类问题,能瞬间让系统工程师的后背发凉。

不是 CPU 占用高,也不是内存泄漏——那些问题虽然恼人,但至少系统还活着,你还能连上 SSH,还能看日志,还能像医生观察病人一样观察它。

最可怕的是那种突然的死寂。屏幕上的光标不动了,心跳包断了,甚至显示器直接黑屏。系统不仅坏了,而且坏得很难看——连最后一声遗言都没来得及留下。

我们在这一章要处理的,正是这种噩梦场景:内核崩溃

当内核代码出错时,没有所谓的「异常处理」能兜得住。进程崩溃只会伤及自身,但内核崩溃意味着整个世界的崩塌。然而,内核并不会一声不吭地死去,它会留下一种充满愤怒的诊断信息——Kernel Oops

读懂 Oops,就像是在案发现场解读凶手留下的密码。它晦涩、充满十六进制地址、夹杂着各种硬件寄存器的术语,让人看一眼就想关掉终端。但如果你掌握了方法,这些乱码其实会极其精确地告诉你:哪个指针写了空、哪一行汇编指令触发了非法访问、以及究竟是哪位同事的代码导致了这场灾难。

更棒的是,Linux 内核社区为了让我们这些倒霉的工程师能稍微好过一点,早已埋下了一套完整的工具链:从能看透内存布局的 procmap,到能像侦探一样把机器码还原成源码行的 decodecode,再到能在系统彻底挂掉前把日志通过网络抛出来的 netconsole

这一章,我们不仅要学会怎么看懂内核的「遗言」,还要搭建一套实战环境,亲手制造一次崩溃,然后用工具把它解剖得清清楚楚。

准备好进 ICU 了吗?我们开始。


7.1 技术准备

在开始把内核搞崩之前,得先把工具箱准备好。

好消息是,我们的基础工作空间配置和第 1 章完全一样。如果你已经跟着前面的章节搭建好了开发环境、配好了内核源码树,那这里可以直接跳过——不用折腾新的虚拟机,也不用重装系统。

所有的示例代码都放在这本书的 GitHub 仓库里,你可以直接 clone 下来:

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

既然我们要深入内核的内存布局和崩溃现场,有一个新工具非常重要——procmap

你在第 1 章已经见过 /proc/<pid>/maps 了,那是个纯文本文件,虽然信息全,但读起来真的很费劲,尤其是面对几百行内存映射的时候,眼睛容易看花。procmap 是一个专门的可视化工具,它能把进程的地址空间——包括用户空间和内核空间——画成一张清晰的地图。在接下来我们将要处理的「空指针解引用」和「内核态 Oops」案例中,这张地图就是你的导航仪,能帮你一眼看出虚拟地址到底对应了哪里。

等到后面分析 NULL 陷阱页的时候,你会感谢这个工具的——它能让你直观地看到内核是如何把进程地址空间底部的第一页「封锁」起来的。

除此之外,为了能让后面的实验顺利跑起来,请再次确认你的系统里已经安装了标准的编译工具链。虽然大部分工作我们都在 x86_64 上做,但如果你想尝试一下跨平台的感觉,比如在 ARM 板子上复现崩溃,确保你的交叉编译工具链也是就绪的。

工具就位,代码就位。接下来,我们要干点坏事了——主动给内核挖个坑,看看它怎么掉进去。