3. Bootloader阶段:GRUB/uboot如何加载内核与initramfs镜像
Bootloader 是系统启动流程中承上启下的关键环节。它负责初始化硬件、建立内存映射,并将内核与 initramfs 镜像从存储介质加载到内存中,最终将控制权移交给内核。本章深入解析 GRUB(x86/ARM64 服务器)与 U-Boot(嵌入式设备)两种主流 Bootloader 的加载机制。
3.1 Bootloader 的核心职责与加载流程
无论 GRUB 还是 U-Boot,在加载内核与 initramfs 时都遵循以下通用步骤:
- 设备初始化:初始化串口、时钟、内存控制器、存储控制器(MMC/SATA/NVMe)。
- 存储驱动加载:加载文件系统驱动(如 ext4、FAT、btrfs)或原始块设备读取能力。
- 镜像定位:根据预配置的路径或分区表,找到内核镜像(vmlinuz / Image)与 initramfs 文件。
- 内存加载:将内核与 initramfs 拷贝到约定的物理内存地址。
- 参数传递:设置内核启动参数(bootargs / cmdline),包括根设备、控制台、DTB 地址等。
- 跳转执行:将 CPU 寄存器设置为入口地址,跳转到内核起始代码。
bootm 命令处理打包的镜像格式(如 uImage、fitImage)。
3.2 GRUB 加载机制详解
GRUB(GRand Unified Bootloader)是 x86/ARM64 平台上最常用的 Bootloader,支持 UEFI 与 Legacy BIOS 两种模式。其加载内核与 initramfs 的核心机制如下:
3.2.1 镜像定位与读取
GRUB 通过 grub.cfg 配置文件中的 linux 和 initrd 命令指定内核与 initramfs 路径。GRUB 使用自身的文件系统驱动(如 ext2、ext4、btrfs、xfs)直接从磁盘分区读取文件。
# grub.cfg 典型配置片段
menuentry "Linux Kernel" {
set root=(hd0,gpt2)
linux /vmlinuz-6.1.0 root=/dev/sda3 console=tty0 console=ttyS0,115200
initrd /initramfs-6.1.0.img
}
boot_params 结构中读取 initramfs 的起始地址与大小。
3.2.2 启动参数传递
GRUB 通过 setup_header 和内核协议(Linux Boot Protocol)传递参数。关键参数包括:
| 参数 | 来源 | 说明 |
|---|---|---|
root= |
grub.cfg 命令行 | 指定根文件系统设备(如 /dev/sda3 或 UUID=xxx) |
initrd= |
grub.cfg 命令行 | initramfs 文件路径(GRUB 自动转换为内存地址) |
console= |
grub.cfg 命令行 | 指定控制台设备(如 ttyS0,115200) |
quiet |
grub.cfg 命令行 | 减少内核启动日志输出 |
3.2.3 UEFI 启动流程
在 UEFI 系统上,GRUB 作为 UEFI 应用程序(grubx64.efi)运行。UEFI 固件负责加载 GRUB,GRUB 再加载内核与 initramfs。关键区别在于:
- UEFI 使用 GPT 分区表,GRUB 通过
EFI System Partition读取配置文件。 - UEFI 提供
LoadImage/StartImage服务,GRUB 可直接调用。 - initramfs 通过 UEFI 的
LoadFile协议或 GRUB 的文件系统驱动加载。
3.3 U-Boot 加载机制详解
U-Boot 是嵌入式系统(ARM、RISC-V、MIPS)的事实标准 Bootloader。其加载方式更加灵活,支持多种镜像格式与存储介质。
3.3.1 镜像格式与加载命令
U-Boot 支持三种主要的镜像格式:
| 格式 | 命令 | 说明 |
|---|---|---|
| uImage | bootm |
传统格式,包含 U-Boot 头部(CRC、加载地址、入口点) |
| fitImage | bootm |
Flattened Image Tree,支持多配置、签名验证、DTB 打包 |
| zImage / Image | bootz |
直接加载原始内核镜像(ARM 平台常用) |
# U-Boot 加载内核与 initramfs 典型命令序列
# 从 MMC 分区加载内核与 DTB
mmc dev 0
fatload mmc 0:1 0x42000000 kernel.itb
fatload mmc 0:1 0x44000000 board.dtb
# 设置启动参数
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait'
# 启动 fitImage(包含内核、DTB、initramfs)
bootm 0x42000000#conf@board1
# 或分别加载内核与 initramfs
fatload mmc 0:1 0x42000000 zImage
fatload mmc 0:1 0x43000000 initramfs.cpio.gz
bootz 0x42000000 0x43000000 0x44000000
3.3.2 内存布局与地址约定
U-Boot 使用固定的内存地址加载各组件。典型的内存布局如下:
| 组件 | 典型加载地址 | 说明 |
|---|---|---|
| 内核(zImage/Image) | 0x42000000 |
ARM 平台常用起始地址,需避开 U-Boot 自身占用区域 |
| initramfs | 0x43000000 |
紧接内核之后,需保证不重叠 |
| DTB | 0x44000000 |
设备树二进制,通常放在最高地址 |
| U-Boot 自身 | 0x40000000 附近 |
由板级配置决定,通常位于低端内存 |
bootm 或 bootz 之前,使用 fdt addr 和 iminfo 检查各镜像的加载地址是否合理。
3.3.3 fitImage 的优势
fitImage 是 U-Boot 推荐的现代镜像格式,它将内核、initramfs、DTB 和配置打包在一个文件中,支持:
- 多配置选择:同一镜像可包含多个板级配置,通过
#conf@board1选择。 - 哈希校验与签名:支持 SHA256 哈希和 RSA 签名验证,防止镜像被篡改。
- 自动内存布局:U-Boot 根据 fitImage 头部信息自动将各组件加载到指定地址。
# fitImage 的 ITS 配置文件示例(kernel.its)
/dts-v1/;
/ {
description = "Linux kernel with initramfs and DTB";
#address-cells = <1>;
images {
kernel {
description = "Linux kernel";
data = /incbin/("./zImage");
type = "kernel";
arch = "arm";
os = "linux";
compression = "none";
load = <0x42000000>;
entry = <0x42000000>;
hash-1 { algo = "sha256"; };
};
initramfs {
description = "initramfs";
data = /incbin/("./initramfs.cpio.gz");
type = "ramdisk";
arch = "arm";
os = "linux";
compression = "gzip";
load = <0x43000000>;
hash-1 { algo = "sha256"; };
};
fdt-board1 {
description = "Flattened Device Tree for board1";
data = /incbin/("./board1.dtb");
type = "flat_dt";
compression = "none";
hash-1 { algo = "sha256"; };
};
};
configurations {
default = "conf@board1";
conf@board1 {
description = "Board 1 configuration";
kernel = "kernel";
ramdisk = "initramfs";
fdt = "fdt-board1";
signature-1 { algo = "sha256,rsa2048"; };
};
};
};
3.4 GRUB 与 U-Boot 对比总结
| 特性 | GRUB | U-Boot |
|---|---|---|
| 主要平台 | x86, ARM64 (服务器) | ARM, RISC-V, MIPS (嵌入式) |
| 固件接口 | UEFI / Legacy BIOS | 裸机 / 自定义板级初始化 |
| 文件系统支持 | ext4, btrfs, xfs, FAT, NTFS | FAT, ext4, ubifs, squashfs |
| 镜像格式 | vmlinuz + initramfs (独立文件) | uImage, fitImage, zImage |
| 参数传递 | 内核协议 (setup_header) | bootargs 环境变量 + DTB |
| 安全启动 | UEFI Secure Boot + shim | fitImage 签名 + 可信执行环境 |
| 调试手段 | GRUB 命令行 + 串口输出 | U-Boot 命令行 + 串口 + JTAG |
3.5 常见问题与排查思路
检查
grub.cfg 中的 set root 是否正确指向包含内核的分区。使用 GRUB 命令行执行 ls 列出可用分区,ls (hd0,gpt2)/ 查看文件列表。
确认
bootargs 中的 root= 参数与 initramfs 中的根设备名称一致。检查 initramfs 是否包含必要的存储驱动(如 mmc_block、ahci)。
检查 initramfs 的压缩格式是否与内核支持的格式匹配。U-Boot 下使用
iminfo 0x43000000 查看镜像头部信息;GRUB 下检查 initrd 文件是否完整。
3.6 小结
Bootloader 阶段的核心任务是将内核与 initramfs 从存储介质安全、高效地加载到内存,并传递正确的启动参数。GRUB 依赖 UEFI 服务和文件系统驱动,适合通用计算平台;U-Boot 更贴近硬件,支持灵活的镜像格式与内存布局,适合嵌入式场景。理解两者的加载机制与内存地址约定,是排查启动失败、根文件系统挂载错误的基础。