引导加载程序阶段:GRUB2的stage1、stage1.5、stage2加载过程详解

好,咱们接着往下走。上一章我们聊了BIOS/UEFI怎么把控制权交出来,现在轮到真正的重头戏了——GRUB2。说实话,我当年刚接触内核启动时,觉得GRUB就是个选系统的菜单,没啥技术含量。直到有一次我在一个嵌入式项目里,系统死活起不来,最后发现是stage1.5的扇区位置被覆盖了……嗯,从那以后我再也不敢小看这个“菜单程序”了。

GRUB2的加载过程,说白了就是一场“三级跳远”。它把自己拆成三个阶段,每个阶段都只做一件事:加载下一阶段。为什么要这么折腾?你想想看,BIOS只给了你512字节的空间(MBR),这点地方连个像样的文件系统驱动都塞不下,更别说加载内核了。所以GRUB2的设计者想了个办法——分步走,逐步扩大能力。

stage1:那512字节的“小纸条”

stage1,也叫boot.img,它被塞在MBR的前446字节里(剩下的64字节是分区表,2字节是魔数0x55AA)。这446字节能干点啥?我告诉你,它连个完整的文件系统都认不出来。它的任务极其简单:

  • 找到stage1.5或stage2的位置
  • 把控制权交给它

具体怎么找?stage1里硬编码了一个扇区地址。这个地址指向磁盘上某个特定位置,通常是MBR之后的那几个扇区。为什么是硬编码?因为stage1太小了,小到连解析分区表都费劲,更别说遍历文件系统了。

核心要点:stage1不关心文件系统,它只认“扇区号”。你给它一个LBA地址,它就老老实实去读那个扇区。这也是为什么GRUB2安装时,必须把stage1.5写到MBR之后的“保留扇区”里。

我记得有一次调试一个UEFI+GRUB2的混合启动问题,发现stage1加载后直接跳到了0地址,系统瞬间重启。查了半天,原来是stage1里硬编码的扇区地址被分区工具给挪了位置。嗯,这种坑踩过一次就记住了。

stage1.5:文件系统的“翻译官”

stage1.5,也叫core.img的一部分,它被放在MBR之后、第一个分区之前的那段空间里。这段空间通常有32KB到64KB,对于stage1.5来说,足够了。

stage1.5的核心价值是什么?它让GRUB2认识了文件系统。你想想看,stage1只能读扇区,但stage1.5可以读文件了。它内置了各种文件系统的驱动:ext4、NTFS、Btrfs、XFS……说白了,它就是个“文件系统翻译官”。

加载流程是这样的:

  1. stage1从MBR后的固定扇区读取stage1.5
  2. stage1.5初始化文件系统驱动
  3. stage1.5在文件系统中找到stage2(通常位于/boot/grub/目录下)
  4. 加载stage2到内存,跳转执行

个人经验:如果你用的是LVM或LUKS加密分区,stage1.5可能会加载失败。为什么?因为stage1.5不认识LVM的逻辑卷结构。这种情况下,GRUB2会把stage1.5和stage2合并成一个“大core.img”,直接塞到MBR后的保留区域里。我建议你在做加密根分区时,一定要用grub-install --force-extra-removable选项。

stage2:真正的“大管家”

stage2,也叫grubx64.elf或core.img的主体部分,它才是我们熟悉的那个GRUB菜单。stage2的大小通常在几百KB到几MB不等,它包含了:

  • 完整的文件系统驱动
  • 设备驱动(磁盘、键盘、显示等)
  • 模块加载器
  • 配置解析器(grub.cfg)
  • 用户交互界面(菜单)

stage2加载完成后,它会做以下几件事:

  1. 读取/boot/grub/grub.cfg配置文件
  2. 解析配置,生成启动菜单
  3. 等待用户选择(或超时自动选择)
  4. 根据菜单项加载内核和initramfs
  5. 跳转到内核入口点

注意:stage2加载内核时,并不是直接把vmlinuz文件整个读到内存就完事了。它需要解析ELF格式,找到入口点,还要处理压缩格式(比如bzImage)。我曾经遇到过一个问题:内核镜像被strip过,GRUB2解析ELF时找不到符号表,直接报错。后来我改用make install而不是手动拷贝,问题就解决了。

整个流程的SVG示意图

下面这张图,我画了GRUB2三级加载的完整流程。你可以对照着看,每个阶段干了什么,数据流怎么走的。

GRUB2 三级加载流程 stage1 (boot.img) MBR前446字节 硬编码扇区地址 读取扇区 stage1.5 (core.img部分) MBR后保留扇区 初始化文件系统驱动 解析文件系统 stage2 (core.img主体) /boot/grub/目录 加载grub.cfg 解析配置 grub.cfg解析 生成启动菜单 用户选择 加载内核+initramfs 解析ELF/bzImage 跳转执行 内核入口点 图例 stage1 (boot.img) stage1.5 (core.img部分) stage2 (core.img主体)

几个容易踩的坑

做内核启动调试这么多年,我总结了几条关于GRUB2的“血泪教训”:

问题场景 原因 解决方案
GRUB2安装后,启动直接黑屏 stage1.5被分区工具覆盖 使用grub-install --force重新安装,并检查分区对齐
加密根分区无法启动 stage1.5不认识LUKS/LVM 使用grub-install --removable生成完整core.img
内核镜像更新后,GRUB2找不到新内核 grub.cfg未更新 运行update-grubgrub-mkconfig
UEFI模式下GRUB2报"no such partition" EFI分区未正确挂载 确保EFI分区挂载到/boot/efi,且文件系统为FAT32

避坑指南:我曾经在一个生产服务器上,因为磁盘分区表被意外修改,导致stage1.5所在的扇区被划给了某个分区。结果服务器重启后,GRUB2直接卡在"Loading stage1.5"不动了。后来我养成了一个习惯:每次修改分区表后,一定重新执行grub-install /dev/sda。嗯,这个习惯救了我好几次。

好了,GRUB2的三级加载就聊到这儿。你可能会问:为什么不用UEFI直接加载内核?嗯,这个问题我们后面会专门讲。但简单说一句:GRUB2的存在,让多系统启动、内核参数传递、救援模式这些功能变得极其灵活。没有它,你每次换内核都得重新烧写固件,想想就头疼。


专注资料整理