OS开发笔记(2)——GRUB启动流程
GRUB如何突破实模式的限制
BIOS与MBR引导机制
BIOS会在启动时,检查逻辑0扇区(即硬盘的第一个扇区)的结尾是否存在标志 0x55
, 0xaa
,以此判断该扇区是否可引导。按照传统的MBR(Master Boot Record)引导流程,逻辑0扇区包含一个特殊的引导程序。这个程序的任务是检查位于逻辑0扇区偏移 0x1BE
处的分区表。分区表最多可以记录四个分区,这也是为什么 MBR 分区的硬盘只能有四个主分区的原因。
在分区表中,每个分区可以有一个“可引导”标记。MBR引导程序会根据某种规则选取一个被标记为“可引导”的分区,将该分区的第一个扇区(称为VBR,Volume Boot Record)加载到内存中(一般加载到地址 0x7C00
以保证兼容性)。随后,程序跳转到VBR,由VBR负责加载并启动该分区中的操作系统。
MBR和VBR的局限性
然而,MBR和VBR的大小都仅有一个扇区,也就是512字节,这对稍微复杂的引导程序来说已经不够用。此外,在16位实模式下,CPU的寻址范围最大只有1MB(实际可用空间还小于1MB,需考虑BIOS和其他数据占用)。因此,对于体积超过1MB或需要加载到1MB以上地址的内核文件,MBR和VBR的能力不足。
16位的寻址限制
切换到保护模式虽然可以突破1MB寻址范围的限制,但由于仅实模式支持调用BIOS功能,在保护模式下无法直接调用BIOS来实现硬盘驱动和文件系统管理。这就需要自行实现这些功能,极大地增加了引导程序的复杂度。为了避免在引导内再实现一边磁盘驱动和文件系统,我选择了使用GRUB。
GRUB的解决方案
在GRUB中,将被称为stage1.5
阶段的GRUB核心加载器(从磁盘启动是diskboot
,从光盘是cdboot
等等)存放在存放磁盘中的某个空闲空间(默认是MBR后的一个扇区),一个定制的MBR引导程序(如 boot.S
,编译后生成 boot.img
)负责加载这一阶段的程序到内存中。
这时候就又出现一个问题,怎么存放GRUB核心呢?GRUB提供了两种方式:
- 使用磁盘未分配空间
GRUB将包含核心功能的core.img
写入硬盘上未被分区表划分的空闲空间。 - 基于文件系统引导
core.img
被当作一个普通文件存储在文件系统中,由MBR程序加载到内存中。
GRUB如何找到core.img
如果采用基于文件系统的方式,问题在于MBR程序如何知道 core.img
的具体位置。GRUB 的core.img
是通过grub-mkimage
命令生成的,它由多个部分组成,包括:
- diskboot:负责从磁盘加载
core.img
的第一部分,大小为一个扇区。 - decompresser:用于解压
grub-core
,以支持压缩后的存储形式。 - core:核心程序模块,可以根据需要加载各种功能扩展(grub模块)。
当执行 grub-install
时,工具会自动将 diskboot
的位置写入 boot.img
中。diskboot
的末尾包含一个 blocklist
,用于记录 grub-core
的各个部分在磁盘上的位置。这样,diskboot
可以顺利加载完整的 grub-core
。
GRUB的引导过程
综上,GRUB 2 的启动流程如下:
- MBR引导
BIOS 加载 MBR(即boot.img
),运行其中的引导程序。 - 加载diskboot
MBR 程序根据记录的位置加载diskboot
。 - 加载并解压grub-core
diskboot
根据blocklist
读取并加载grub-core
,必要时解压。 - 启动操作系统内核
grub-core
最终加载目标操作系统的内核文件,并完成引导。
感想
稍微深入了解过GRUB后,感觉像是迷你内核一样,功能这么丰富。