Linux内核启动源码分析与初始化故障排查 - 第4节

4.1 GRUB2 配置文件 (grub.cfg) 的结构与语法

GRUB2 的核心配置文件为 /boot/grub/grub.cfg(或 /boot/grub2/grub.cfg)。该文件由 grub2-mkconfig 工具根据 /etc/default/grub 和 /etc/grub.d/ 目录下的脚本自动生成。理解其结构是分析内核加载流程的第一步。

4.1.1 配置文件层级结构

grub.cfg 采用嵌套的菜单项结构,核心语法包括:

  • 菜单项定义:menuentry "标题" { ... }
  • 子菜单定义:submenu "标题" { ... }
  • 全局设置:set default="0"、set timeout=5
  • 加载模块:insmod ext2、insmod lvm
  • 内核与 initrd 加载:linux、initrd 命令
关键点:grub.cfg 是只读文件,用户不应直接编辑。所有自定义修改应通过 /etc/default/grub 和 /etc/grub.d/40_custom 完成,然后执行 grub2-mkconfig -o /boot/grub2/grub.cfg 重新生成。

4.1.2 典型菜单项结构

以下是一个标准 Linux 内核启动菜单项的完整示例:

menuentry 'CentOS Linux (5.10.0-60.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os {
    load_video
    set gfxpayload=keep
    insmod gzio
    insmod part_msdos
    insmod ext2
    set root='hd0,msdos1'
    linux   /vmlinuz-5.10.0-60.el7.x86_64 root=/dev/mapper/cl-root ro crashkernel=auto rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet
    initrd  /initramfs-5.10.0-60.el7.x86_64.img
}
命令/指令 作用说明
load_video 加载视频驱动模块,支持图形化显示
set gfxpayload=keep 保持图形模式,传递给内核
insmod gzio 加载 gzip 解压模块,支持压缩的内核/initrd
insmod part_msdos 加载 MBR 分区表支持
insmod ext2 加载 ext2/3/4 文件系统驱动
set root='hd0,msdos1' 设置根设备为第一块硬盘的第一个分区
linux 指定内核映像路径及启动参数
initrd 指定 initramfs 映像路径

4.2 菜单显示与用户交互

GRUB2 在加载配置文件后,会解析所有 menuentry 和 submenu 并构建启动菜单。用户可通过键盘选择、编辑启动项,或进入命令行模式。

4.2.1 菜单显示流程

提示:GRUB2 支持图形化终端(gfxterm)和文本终端。默认优先使用图形终端,若显卡不支持则回退到文本模式。
GRUB2 菜单显示与用户交互流程 加载 grub.cfg 解析 menuentry / submenu 显示启动菜单 选择菜单项 按 e 编辑 执行 linux / initrd 用户交互选项 e=编辑当前项 c=命令行

4.2.2 菜单编辑与参数传递

在菜单界面按 e 键可进入编辑模式,用户可临时修改当前菜单项的启动参数。常见编辑场景包括:

  • 修改 root 参数:如将 root=/dev/sda1 改为 root=/dev/sdb1
  • 添加调试参数:如追加 single 进入单用户模式,或 rd.break 中断 initramfs
  • 禁用 rhgb/quiet:移除这两个参数可查看完整的内核启动日志
注意:编辑模式下的修改仅对本次启动有效,重启后恢复。若需永久修改,应编辑 /etc/default/grub 中的 GRUB_CMDLINE_LINUX 变量。

4.3 内核加载过程

当用户选择某个菜单项后,GRUB2 开始执行 linux 命令,将内核映像从磁盘加载到内存,并准备启动参数。

4.3.1 内核映像的定位与加载

GRUB2 根据 set root 指定的分区定位内核文件,然后通过文件系统驱动读取 /vmlinuz-* 文件。加载过程分为两个阶段:

  1. 读取内核头部:解析 ELF 或 bzImage 格式,获取入口地址、段信息
  2. 加载内核到内存:将内核代码段、数据段复制到指定物理地址(通常为 0x100000 即 1MB 处)
GRUB2 加载内核与 initramfs 到内存的布局 磁盘 (hd0,msdos1) /vmlinuz-5.10.0 /initramfs-5.10.0.img grub.cfg 读取 读取 系统内存 (RAM) 内核代码段 0x100000 ~ 0x200000 内核数据段 0x200000 ~ 0x300000 initramfs 0x300000 ~ 0x500000 启动参数区 root=/dev/mapper/cl-root ro crashkernel=auto rd.lvm.lv=cl/root rhgb quiet BOOT_IMAGE=/vmlinuz-...

4.3.2 启动参数的传递

linux 命令中 root= 之后的全部内容构成内核命令行(kernel command line)。GRUB2 将这些参数按约定格式写入内存中的特定位置,内核在启动初期通过 boot_params 结构体读取。

参数类别 示例 说明
根文件系统 root=/dev/mapper/cl-root 指定内核挂载的根设备
挂载选项 ro 以只读方式挂载根文件系统(后续由 init 重新挂载为 rw)
LVM 逻辑卷 rd.lvm.lv=cl/root initramfs 阶段激活 LVM 卷
调试与显示 rhgb quiet 图形化启动(Red Hat Graphical Boot),隐藏详细日志
内存配置 crashkernel=auto 为 kdump 预留内存

4.4 initrd / initramfs 的加载与传递

在 linux 命令之后,GRUB2 执行 initrd 命令,将 initramfs 映像加载到内存中,并将内存地址信息传递给内核。

4.4.1 initramfs 的作用

initramfs(initial RAM filesystem)是一个压缩的 cpio 归档文件,包含内核启动初期所需的驱动、工具和脚本。其核心使命是:

  • 加载存储驱动:如 SCSI、NVMe、SATA 驱动,使内核能访问根文件系统
  • 激活卷管理:如 LVM、MD RAID、dm-crypt
  • 挂载根文件系统:执行 pivot_root 切换到真正的根

4.4.2 加载与解压流程

  1. GRUB2 读取 initramfs 文件:从磁盘读取 /initramfs-*.img 到内存缓冲区
  2. 记录内存地址:将 initramfs 的起始地址和大小写入 boot_params 结构体
  3. 内核解压:内核启动后,从指定地址读取 initramfs,解压到 rootfs 中
  4. 执行 /init:内核挂载 rootfs 后,执行其中的 /init 脚本
性能指标:典型的 initramfs 大小为 20~50 MB,GRUB2 加载耗时约 0.5~2 秒(取决于磁盘速度)。内核解压 initramfs 通常在 0.1~0.3 秒内完成。

4.4.3 多 initrd 支持

GRUB2 支持同时加载多个 initrd 映像,通过 initrd 命令后跟多个路径实现:

initrd /initramfs-5.10.0-60.el7.x86_64.img /microcode.img

其中 microcode.img 包含 CPU 微码更新,内核会优先处理微码,再加载主 initramfs。

4.5 启动流程总结

从 GRUB2 配置文件解析到内核与 initramfs 加载完成,整个流程可归纳为以下关键步骤:

步骤 操作 关键组件
1 读取并解析 grub.cfg GRUB2 核心、文件系统驱动
2 显示启动菜单,等待用户选择 gfxterm / 文本终端
3 执行 linux 命令,加载内核到内存 vmlinuz、boot_params
4 执行 initrd 命令,加载 initramfs initramfs.img、cpio 归档
5 跳转到内核入口点,移交控制权 startup_32 / startup_64
学习建议:在虚拟机中执行 grub2-editenv list 查看当前默认启动项,使用 grub2-set-default 切换默认项。通过 cat /proc/cmdline 查看实际传递给内核的命令行参数,与 grub.cfg 中的配置进行对比验证。