3、引导加载程序:GRUB2的配置与启动流程
系统上电后,BIOS/UEFI 把控制权交给了引导加载程序。在 Linux 世界里,GRUB2 是绝对的主角。说实话,我见过不少开发者把 GRUB2 当成一个黑盒子——装系统时自动配好,出问题时两眼一抹黑。我个人习惯是,必须搞懂它到底干了什么。
GRUB2 的全称是 GRand Unified Bootloader version 2。它负责三件事:加载内核、传递参数、交出控制权。听起来简单,但里面的门道不少。
核心要点:GRUB2 的启动流程分为三个阶段——boot.img → core.img → /boot/grub 下的配置和模块。每个阶段都有明确的职责边界。
3.1 GRUB2 的启动阶段拆解
GRUB2 不是一口气把所有代码都加载到内存的。它受限于 BIOS/UEFI 的初始环境,必须分步走。我把它拆成三个阶段来讲。
阶段一:boot.img(第一扇区)
BIOS/UEFI 读取磁盘的第一个扇区(MBR 或 GPT 保护分区),共 512 字节。这里面放的就是 boot.img。它小得可怜,唯一能干的事就是找到 core.img 的位置,然后跳转过去。
嗯,这里要注意:boot.img 本身不识别文件系统。它直接读取磁盘的绝对扇区号。所以 core.img 必须放在一个固定的、连续的磁盘区域里。这就是为什么你运行 grub-install 时,它会往 MBR 后面的扇区里写数据。
阶段二:core.img(核心映像)
core.img 是 GRUB2 真正的起点。它包含了文件系统驱动、磁盘驱动、以及一些基础模块。有了它,GRUB2 才能识别 /boot 分区上的文件系统,读取配置文件。
我记得有一次排查一个启动失败的问题,内核明明在 /boot 下,GRUB2 就是找不到。后来发现是 core.img 里缺少了对应文件系统的模块——当时用的是 Btrfs,而默认安装只带了 ext4 模块。踩过这个坑之后,我每次装系统都会检查 /boot/grub/ 下的模块列表。
阶段三:grub.cfg 与菜单
core.img 加载完成后,GRUB2 会去读取 /boot/grub/grub.cfg。这个文件是真正的配置中心。它定义了菜单项、内核路径、启动参数、以及各种钩子脚本。
你想想看,grub.cfg 其实不是手写的。它是通过 grub-mkconfig 命令自动生成的。但自动生成不代表你不能改——我经常在 /etc/default/grub 里加一些自定义参数,然后重新生成配置。
个人技巧:我习惯在 /etc/default/grub 里设置 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" 之外,再加一个 GRUB_TIMEOUT=5 控制菜单等待时间。调试内核时,我会把 quiet 去掉,换成 verbose,这样启动日志会直接打印到屏幕上。
3.2 grub.cfg 配置文件深度解析
我们来看一个典型的 grub.cfg 片段。别被它的长度吓到,核心结构其实很清晰。
###
### BEGIN /etc/grub.d/00_header ###
set default="0"
set timeout=5
load_video
insmod gzio
insmod part_gpt
insmod ext2
set root='hd0,gpt2'
search --no-floppy --fs-uuid --set=root 1234-5678-90ab-cdef
### END /etc/grub.d/00_header ###
###
### BEGIN /etc/grub.d/10_linux ###
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-1234-5678-90ab-cdef' {
recordfail
load_video
gfxmode $linux_gfx_mode
insmod gzio
if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi
insmod part_gpt
insmod ext2
set root='hd0,gpt2'
search --no-floppy --fs-uuid --set=root 1234-5678-90ab-cdef
linux /vmlinuz-5.15.0-91-generic root=UUID=1234-5678-90ab-cdef ro quiet splash $vt_handoff
initrd /initrd.img-5.15.0-91-generic
}
### END /etc/grub.d/10_linux ###
这个文件由多个脚本片段拼接而成。每个 /etc/grub.d/ 下的脚本负责生成一部分内容。我挑几个关键点说说:
set root:指定根设备。注意,这里的 root 是 GRUB2 自己的根,不是 Linux 内核的根。它告诉 GRUB2 去哪里找内核和 initrd 文件。search:通过 UUID 或标签查找分区。这是最可靠的方式,因为磁盘设备名(如/dev/sda1)可能会变。linux:指定内核文件路径和启动参数。root=UUID=...是传给内核的参数,告诉内核根文件系统在哪。initrd:指定初始 RAM 磁盘的路径。这个我们下一章会详细讲。
避坑指南:我曾经遇到过一个问题:更新内核后,grub.cfg 里出现了两个 menuentry,但默认启动的还是旧内核。原因是 /etc/default/grub 里的 GRUB_DEFAULT 设置成了 saved,而上次启动时选的是旧内核。解决方案是手动运行 grub-set-default 0 或者直接改成 GRUB_DEFAULT="Ubuntu" 按名称匹配。
3.3 GRUB2 的配置生成体系
grub.cfg 不是手写的,而是通过 grub-mkconfig 调用 /etc/grub.d/ 下的脚本生成的。这个设计很巧妙——每个脚本负责一个独立的功能模块。
| 脚本文件 | 功能 | 我常用的修改 |
|---|---|---|
00_header |
设置全局变量、加载基础模块 | 调整 GRUB_TIMEOUT、GRUB_DEFAULT |
10_linux |
扫描 /boot 下的内核,生成菜单项 |
添加自定义内核参数 |
30_os-prober |
探测其他操作系统(如 Windows) | 我一般会禁用,避免误探测 |
40_custom |
用户自定义菜单项 | 添加内存测试、救援模式等 |
为什么要有这个体系?说白了,是为了可维护性。你不需要直接编辑 grub.cfg,因为下次 update-grub 一跑,你的修改就没了。正确的做法是改 /etc/default/grub 或者 /etc/grub.d/ 下的脚本。
3.4 内核加载与参数传递
当你在 GRUB2 菜单里选中一个条目并按回车后,GRUB2 会执行 linux 和 initrd 命令。这两个命令干的事很关键:
- 加载内核:GRUB2 把
/boot/vmlinuz-*文件读入内存。内核被加载到固定地址后,GRUB2 会解析内核的头部信息,确认格式正确。 - 设置启动参数:
linux命令后面的所有字符串都会被传递给内核。内核在启动时会解析这些参数,比如root=、quiet、splash等。 - 加载 initrd:
initrd命令把初始 RAM 磁盘加载到内存。内核启动后会把它挂载为临时的根文件系统。 - 跳转到内核入口:GRUB2 执行
boot指令,把控制权交给内核。从此,GRUB2 的任务就结束了。
关键参数说明:
root=UUID=...:指定真正的根文件系统。内核在 initrd 阶段会挂载它。ro:以只读方式挂载根文件系统。这是安全措施,防止启动过程中意外写入。quiet:减少内核日志输出。调试时建议去掉。splash:显示启动画面。如果显卡驱动有问题,这个参数会导致黑屏。
3.5 GRUB2 启动流程图
下面这张图把整个流程串起来了。你可以对照着看,每个阶段做了什么,数据流怎么走的。
3.6 常见问题与排查思路
GRUB2 出问题的时候,系统连内核都加载不了。我总结了几种常见场景:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| GRUB2 菜单不出现,直接黑屏 | GRUB_TIMEOUT 为 0,或显卡驱动问题 |
启动时按住 Shift 键强制显示菜单 |
| 选择菜单后报 "File not found" | 内核或 initrd 路径错误,或分区 UUID 变了 | 进入 GRUB2 命令行,用 ls 和 cat 检查文件 |
| 启动后卡在 "Loading initial ramdisk" | initrd 损坏或缺失 | 用 Live CD 启动,重新生成 initrd |
| GRUB2 安装失败,报 "grub-install: error" | 磁盘分区表问题,或缺少 BIOS Boot Partition | 检查 GPT 分区,确保有 bios_grub 标志 |
调试技巧:在 GRUB2 菜单界面按 c 键进入命令行模式。你可以手动执行 ls 列出所有分区,用 set root=(hd0,gpt2) 指定根设备,然后用 linux /vmlinuz-... root=/dev/sda2 手动启动内核。这是最直接的排查方式,比看日志快多了。
GRUB2 的配置和启动流程,说白了就是一套精心设计的接力赛。每个阶段只做自己该做的事,然后把接力棒交给下一棒。搞懂了这个流程,你就能在系统启动出问题时,快速定位是哪个环节掉了链子。