引导加载程序阶段: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……说白了,它就是个“文件系统翻译官”。
加载流程是这样的:
- stage1从MBR后的固定扇区读取stage1.5
- stage1.5初始化文件系统驱动
- stage1.5在文件系统中找到stage2(通常位于/boot/grub/目录下)
- 加载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加载完成后,它会做以下几件事:
- 读取/boot/grub/grub.cfg配置文件
- 解析配置,生成启动菜单
- 等待用户选择(或超时自动选择)
- 根据菜单项加载内核和initramfs
- 跳转到内核入口点
注意:stage2加载内核时,并不是直接把vmlinuz文件整个读到内存就完事了。它需要解析ELF格式,找到入口点,还要处理压缩格式(比如bzImage)。我曾经遇到过一个问题:内核镜像被strip过,GRUB2解析ELF时找不到符号表,直接报错。后来我改用make install而不是手动拷贝,问题就解决了。
整个流程的SVG示意图
下面这张图,我画了GRUB2三级加载的完整流程。你可以对照着看,每个阶段干了什么,数据流怎么走的。
几个容易踩的坑
做内核启动调试这么多年,我总结了几条关于GRUB2的“血泪教训”:
| 问题场景 | 原因 | 解决方案 |
|---|---|---|
| GRUB2安装后,启动直接黑屏 | stage1.5被分区工具覆盖 | 使用grub-install --force重新安装,并检查分区对齐 |
| 加密根分区无法启动 | stage1.5不认识LUKS/LVM | 使用grub-install --removable生成完整core.img |
| 内核镜像更新后,GRUB2找不到新内核 | grub.cfg未更新 | 运行update-grub或grub-mkconfig |
| UEFI模式下GRUB2报"no such partition" | EFI分区未正确挂载 | 确保EFI分区挂载到/boot/efi,且文件系统为FAT32 |
避坑指南:我曾经在一个生产服务器上,因为磁盘分区表被意外修改,导致stage1.5所在的扇区被划给了某个分区。结果服务器重启后,GRUB2直接卡在"Loading stage1.5"不动了。后来我养成了一个习惯:每次修改分区表后,一定重新执行grub-install /dev/sda。嗯,这个习惯救了我好几次。
好了,GRUB2的三级加载就聊到这儿。你可能会问:为什么不用UEFI直接加载内核?嗯,这个问题我们后面会专门讲。但简单说一句:GRUB2的存在,让多系统启动、内核参数传递、救援模式这些功能变得极其灵活。没有它,你每次换内核都得重新烧写固件,想想就头疼。