开发者生态
morning
了解 Linux 内核:Linux 内核启动
2026-05-14
1 阅读
valyala
您是否想知道从按下电源按钮到登录屏幕出现之间到底发生了什么?这个间隙(通常是几秒钟)隐藏了计算中最复杂的初始化序列之一。今天我想带你了解一下。这是系列文章中的第一篇,我将尝试与您一起了解 Linux 内核的内部结构。我们将讨论 Linux 如何启动、如何管理进程和内存、如何处理硬件等等。如果您曾经对幕后发生的事情感到好奇,那么您来对地方了。 ⚠️ 快速免责声明 我不是内核专家——我正在大声学习。这里的目标不是深入、详尽的游览,而是一张有用的地图:主要部分是什么以及它们如何组合在一起。对于深潜来说,源头才是真正的老师。本文重点介绍 x86_64 。大局广泛适用,但具体情况因 ARM、RISC-V 等而异。现在让我给你打个比喻,因为这将是一个漫长的旅程,我们需要一条线索来抓住。想象一下我们正在建立一个太空殖民地想象一个贫瘠的星球。没有空气可供呼吸,没有道路,没有建筑物,没有电力,没有通讯。我们用运输船派遣了一支小型先遣队。他们的任务是:将这块岩石变成一个工作殖民地,并在生命支持耗尽之前做到这一点。先遣团队不能只是卸下所有人并开始举办市政厅会议。他们必须按照非常特定的顺序做事。首先是基础知识:确认着陆器没有坠毁,建立应急程序以防出现问题。然后绘制地形图,找到可用资源,留出存储区域。然后调出建筑设备,建造第一个栖息地、电网、通讯塔。然后开始适当的治理:一个殖民地总督,一个处理未来船员抵达的调度办公室,以及一个接管无聊的“保持事物运转”职责的维护人员。最后,他们将其余的殖民者从冷冻睡眠中唤醒,并将该地方的钥匙交给他们。这几乎就是 Linux 内核在启动时所做的事情。引导加载程序是 dropship。你的电脑是一个贫瘠的星球。先遣团队负责 Linux 内核中启动代码的执行——我们将一直跟踪这个团队。到本文结束时,先遣队实际上已经转变为待命维护人员,而全新的文职政府将接管。请耐心听我说——随着我们的讨论,它会变得有意义。这是我们即将进行的艰难旅程:让我们从引导加载程序停止的地方开始。交接:引导加载程序将什么交给我们 因此 GRUB(或您正在使用的任何引导加载程序)将控制权交给了内核。我们实际上需要处理什么?老实说,不多。 CPU 已经在运行,但处于几种模式之一——大致是寄存器的宽度以及内存的工作方式。在 x86 上,这是 16 位实模式、32 位保护模式或 64 位长模式。 UEFI 让我们直接进入长模式;传统 BIOS 通常会让我们处于保护模式。一旦进入阶段 1,我们将处理 CPU 的其余状态(页表、中断)。内存很尴尬。内核加载的 RAM 较低(通常约为 0x1000000 ),但它被编译为在较高的虚拟地址(例如 0xffffffff81000000 )上运行。这种不匹配很快就会困扰我们。还有什么?来自固件的内存映射(x86 上的 E820 映射)告诉我们 RAM 的位置、保留的内容以及 ACPI(高级配置和电源接口)表的位置;一包启动参数(命令行、initrd 位置等);就是这样。没有控制台,没有分配器,没有中断,没有日志。让我们构建一些东西。第 1 阶段:汇编 Trampoline 首先解压内核 首先有一个小问题:引导加载程序交给我们的文件是 bzImage ,其中大部分是压缩的。传送压缩的内核可以在启动期间节省磁盘和内存空间,但 CPU 显然无法执行压缩的字节。因此,运行的第一个代码并不是内核本身,它是一个位于 arch/x86/boot/compressed/ 中的小型解压缩器。它的工作是将真实的内核映像解压到内存中,然后跳转到它。解压缩器还会选择一个随机基地址来加载内核,这就是 KASLR(内核地址空间布局随机化),这让想要猜测内核代码所在位置的攻击者变得更加困难。一旦解压缩完成,控制就会跳转到真正的内核。进入真正的内核 我们登陆的位置取决于引导加载程序。在传统的 32 位启动中,我们从解压器中的startup_32 开始,它必须将 CPU 爬升到 64 位长模式本身 - 构建一个小型页表,其中每个虚拟地址都指向相同的物理地址(身份映射 - 最简单的可能设置),翻转“您现在是 64 位芯片”位,打开分页,然后跳转到startup_64。现代 UEFI 引导加载程序会跳过爬升并直接跳转到startup_64。不管怎样,每一条路都会汇聚于此。我们就像赤裸裸的