3. Bootloader 核心:GRUB2 的 stage1、stage1.5、stage2 加载过程
GRUB2(GRand Unified Bootloader version 2)是现代 Linux 发行版中最主流的引导加载程序。它将引导过程拆分为多个阶段(stage),以克服 x86 平台实模式下的 512 字节 MBR 限制,并支持复杂的文件系统解析与菜单交互。理解 stage1、stage1.5 和 stage2 的加载链条,是分析启动故障与内核加载的前提。
3.1 三阶段架构总览
GRUB2 的核心引导流程分为三个逻辑阶段,每个阶段承担不同的职责,并逐步提升执行环境的能力:
| 阶段 | 存储位置 | 大小限制 | 核心能力 | 执行模式 |
|---|---|---|---|---|
| stage1 | MBR(主引导记录,LBA 0) | 446 字节(引导代码区) | 加载 stage1.5 或 stage2 的起始扇区 | 实模式(16-bit) |
| stage1.5 | MBR 与第一个分区之间的扇区(LBA 1~62)或文件系统嵌入区 | 约 30 KB | 识别文件系统,加载 stage2 核心映像 | 实模式(16-bit) |
| stage2 | 文件系统内(/boot/grub/ 目录) |
无严格限制(通常数百 KB) | 解析配置文件、显示菜单、加载内核与 initrd | 实模式 → 保护模式切换 |
3.2 stage1:MBR 中的“种子”代码
stage1 是 GRUB2 在磁盘上占据的第一个 512 字节扇区(MBR)。它由 boot.img 生成,被写入 MBR 的引导代码区(偏移 0x000~0x1BD)。
stage1 的核心任务:
- 定位 stage1.5 或 stage2: 使用内嵌的 LBA 地址或 CHS 地址,读取后续扇区。
- 加载并跳转: 将 stage1.5 的第一个扇区加载到内存
0x8000处,然后跳转执行。 - 传递磁盘信息: 将当前引导磁盘的驱动器号(DL 寄存器)传递给下一阶段。
stage1 的局限性:
- 无法理解文件系统结构,只能按扇区地址读取。
- 代码空间极度紧张(446 字节),无法包含复杂逻辑。
- 依赖 BIOS 中断(int 13h)进行磁盘 I/O,性能较低。
dd if=/dev/sda bs=512 count=1 | hexdump -C 检查 MBR 前 446 字节是否包含 GRUB 签名(通常以 GRUB 字符串开头)。
3.3 stage1.5:文件系统识别桥接层
stage1.5 由 core.img 的前部分构成,通常存储在 MBR 与第一个分区之间的“嵌入区域”(MBR gap,LBA 1~62,共 31 KB)。它解决了 stage1 无法识别文件系统的根本问题。
stage1.5 的核心能力:
- 文件系统驱动: 包含 ext2/3/4、FAT、NTFS、XFS 等常见文件系统的读取模块。
- 路径解析: 能够根据
/boot/grub/路径找到 stage2 的核心映像文件。 - 加载 stage2: 将 stage2 的完整代码从文件系统读入内存(通常加载到
0x2000或更高地址)。
stage1.5 的构建方式:
GRUB2 在安装时(grub-install)会根据目标磁盘的文件系统类型,动态生成包含对应文件系统驱动的 core.img。例如:
# 查看 core.img 包含的模块
grub2-mkimage -v -o /tmp/core.img ext2 fat part_msdos biosdisk
# 输出会列出每个模块的加载地址和大小
error: unknown filesystem 并进入 rescue shell。
3.4 stage2:完整的引导管理器
stage2 是 GRUB2 的主体,对应 /boot/grub/ 目录下的 grub.cfg 配置文件以及 *.mod 模块文件。它运行在实模式,但具备切换到保护模式的能力。
stage2 的执行流程:
- 初始化控制台: 初始化显示输出(VGA 文本模式或帧缓冲模式)和键盘输入。
- 加载配置文件: 读取
/boot/grub/grub.cfg,解析菜单项、内核参数、模块加载指令。 - 显示菜单: 呈现操作系统选择列表,等待用户选择或超时自动启动。
- 加载内核与 initrd: 根据菜单项配置,将内核映像(vmlinuz)和 initramfs 加载到内存指定位置。
- 切换保护模式并跳转: 设置 A20 地址线、进入保护模式,调用内核的 32 位入口点(startup_32)。
stage2 的内存布局示例:
| 内存地址范围 | 内容 | 大小 |
|---|---|---|
0x0000:0x7C00 |
stage1 加载区(MBR 副本) | 512 B |
0x0000:0x8000 |
stage1.5 加载区 | ~31 KB |
0x0010:0x0000 |
stage2 核心代码 | ~200 KB |
0x0010:0x0000 + 偏移 |
内核映像(vmlinuz) | 数 MB |
0x0010:0x0000 + 偏移 |
initramfs | 数 MB |
3.5 三阶段加载的故障排查要点
当系统无法完成引导时,通过观察 GRUB2 的提示信息可以快速定位故障阶段:
- 无任何输出,直接黑屏: 可能 stage1 损坏或 BIOS 未找到引导设备。使用 Live CD 修复 MBR:
grub2-install /dev/sda。 - 输出
GRUB后卡住: stage1 已执行,但无法加载 stage1.5。检查 MBR gap 是否被覆盖,或文件系统驱动不匹配。 - 进入 rescue shell(
grub rescue>): stage1.5 已加载但无法找到 stage2 或grub.cfg。使用ls命令探测可用分区,手动指定内核路径:set root=(hd0,msdos1)、linux /boot/vmlinuz-xxx root=/dev/sda1。 - 菜单显示但选择后重启: stage2 已加载但内核映像损坏或启动参数错误。在菜单按
e编辑启动项,检查linux和initrd路径是否正确。
grub2-editenv list 查看 GRUB2 的环境变量(如 next_entry、saved_entry),这些变量影响 stage2 的默认启动项选择。使用 grub2-mkconfig -o /boot/grub/grub.cfg 重新生成配置文件。
3.6 从 stage2 到内核的交接协议
stage2 在跳转内核前,必须遵循 Multiboot 规范(或 Multiboot2)将控制权交给内核。关键交接内容包括:
| 交接项 | 寄存器/内存位置 | 说明 |
|---|---|---|
| 魔数(Magic Number) | EAX = 0x2BADB002 |
标识引导加载程序符合 Multiboot 规范 |
| 引导信息结构体指针 | EBX = 物理地址 | 包含内存布局、模块地址、命令行等 |
| 内核入口点 | EIP = 内核 ELF 入口 | 通常指向 startup_32 或 _start |
| CS 段寄存器 | 32-bit 保护模式代码段 | 基址 0,限长 4 GB |
| A20 地址线 | 已启用 | 确保 1 MB 以上内存可访问 |
内核在 startup_32 入口处首先检查 EAX 中的魔数,如果匹配则使用 EBX 指向的引导信息结构体来初始化内存管理、解析内核参数、解压内核映像。如果魔数不匹配,内核将无法正确识别引导环境,导致 panic。
c 进入命令行模式,使用 multiboot2 命令手动加载内核并观察返回状态。使用 cat 命令查看 /proc/iomem 可以验证内核启动后内存布局是否与 Multiboot 信息一致。