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 实模式 → 保护模式切换
关键洞察: stage1.5 并非在所有场景下都必需。当 BIOS 支持 LBA 模式且 stage1 可以直接读取文件系统时,stage1 可跳过 stage1.5 直接加载 stage2(称为“直接加载”模式)。但在传统 BIOS + MBR 分区表中,stage1.5 是兼容性最好的中间层。

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,性能较低。
调试提示: 如果系统在 MBR 阶段挂起(黑屏无光标闪烁),通常意味着 stage1 代码损坏、MBR 分区表错误或 BIOS 无法读取引导磁盘。可以使用 dd if=/dev/sda bs=512 count=1 | hexdump -C 检查 MBR 前 446 字节是否包含 GRUB 签名(通常以 GRUB 字符串开头)。
GRUB2 stage1 加载 stage1.5 扇区流程 stage1 加载 stage1.5 扇区流程 BIOS 加载 MBR stage1 (boot.img) int 13h 读取 LBA 1~62 stage1.5 (core.img) 跳转至 0x8000 执行 说明:stage1 使用内嵌的 LBA 地址直接读取磁盘扇区,不依赖文件系统。加载完成后通过跳转指令将控制权交给 stage1.5。

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
# 输出会列出每个模块的加载地址和大小
常见故障: 如果 stage1.5 所在的嵌入区域被其他操作系统(如 Windows 的磁盘管理工具)覆盖,或文件系统驱动模块与根分区文件系统不匹配,系统会在 stage1.5 阶段报错 error: unknown filesystem 并进入 rescue shell。

3.4 stage2:完整的引导管理器

stage2 是 GRUB2 的主体,对应 /boot/grub/ 目录下的 grub.cfg 配置文件以及 *.mod 模块文件。它运行在实模式,但具备切换到保护模式的能力。

stage2 的执行流程:

  1. 初始化控制台: 初始化显示输出(VGA 文本模式或帧缓冲模式)和键盘输入。
  2. 加载配置文件: 读取 /boot/grub/grub.cfg,解析菜单项、内核参数、模块加载指令。
  3. 显示菜单: 呈现操作系统选择列表,等待用户选择或超时自动启动。
  4. 加载内核与 initrd: 根据菜单项配置,将内核映像(vmlinuz)和 initramfs 加载到内存指定位置。
  5. 切换保护模式并跳转: 设置 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
GRUB2 stage2 加载内核与 initrd 流程 stage2 加载内核与 initrd 完整流程 stage2 核心启动 解析 grub.cfg 显示启动菜单 加载 vmlinuz 加载 initramfs 设置启动参数 跳转至 startup_32 说明:stage2 在跳转内核前会设置 A20 地址线、构建实模式中断向量表、传递 multiboot 协议结构体给内核。

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。

调试技巧: 在 GRUB2 菜单按 c 进入命令行模式,使用 multiboot2 命令手动加载内核并观察返回状态。使用 cat 命令查看 /proc/iomem 可以验证内核启动后内存布局是否与 Multiboot 信息一致。