4. GRUB2引导加载器:配置文件结构、内核参数传递、签名验证、常见故障场景

好,咱们今天聊聊GRUB2。说实话,在可信启动这条链上,GRUB2是个承上启下的关键角色。它得验证自己,还得验证内核,任何一个环节出问题,机器就起不来。我这些年排查过的启动故障,至少有三成跟GRUB2有关。所以这块内容,你得吃透。

4.1 GRUB2配置文件结构

GRUB2的配置文件,核心就是 /boot/grub2/grub.cfg。但你要注意,这个文件通常不是手写的,而是由 grub2-mkconfig 工具自动生成的。我个人习惯,从来不直接改 grub.cfg,而是改 /etc/default/grub 和 /etc/grub.d/ 目录下的脚本。

为什么?因为 grub.cfg 一更新就会被覆盖,你手动改的东西全没了。我在项目中遇到过有人直接改了 grub.cfg 加内核参数,结果下次内核更新后,参数丢了,系统启动异常。嗯,这是个很典型的坑。

配置文件的结构大致如下:

# /etc/default/grub 核心配置
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true

这里我重点说几个:

  • GRUB_TIMEOUT:菜单等待时间,单位秒。设为0就直接启动,设为-1就无限等待。我建议调试阶段设成5秒以上,免得手忙脚乱。
  • GRUB_CMDLINE_LINUX:这是传给内核的参数,后面会细讲。
  • GRUB_ENABLE_BLSCFG:Boot Loader Specification 配置,新版本RHEL/CentOS 8+ 默认开启。如果你发现改了 /etc/default/grub 但启动菜单没变,八成是这个在作怪。

而 /etc/grub.d/ 目录下的脚本,执行顺序是按文件名数字来的:

脚本 作用
00_header 加载GRUB环境变量,设置默认配置
10_linux 查找当前系统内核,生成启动项
30_os-prober 探测其他操作系统(如Windows)
40_custom 用户自定义启动项,推荐在这里加

改完配置后,记得执行 grub2-mkconfig -o /boot/grub2/grub.cfg 重新生成。我曾经见过有人忘了这步,重启后配置没生效,急得团团转。

4.2 内核参数传递

内核参数,说白了就是告诉Linux内核在启动时该怎么干活。你想想看,内核刚启动时啥也不知道,得靠这些参数来配置内存、设备、安全策略等。

参数通过 GRUB_CMDLINE_LINUX 传递,常见的有:

  • rhgb quiet:Red Hat 图形化启动,隐藏详细日志。排查问题时建议去掉,能看到完整的启动信息。
  • crashkernel=auto:预留内存给kdump用。我遇到过预留太大导致系统内存不足的情况,后来改成手动指定大小。
  • console=tty0 console=ttyS0,115200:同时输出到本地终端和串口。服务器调试时特别有用。
  • selinux=0 enforcing=0:关闭SELinux。注意,这只是临时调试用,生产环境千万别这么干。
  • systemd.unit=emergency.target:直接进入紧急模式,适合修复系统。

这里有个小技巧:你可以在GRUB启动菜单界面按 e 键,临时编辑当前启动项的内核参数。改完后按 Ctrl+x 启动,这次生效,下次重启就没了。非常适合临时调试。

我的经验: 调试内核参数时,先用临时编辑方式验证,确认没问题了再写到配置文件里。这样可以避免改错参数导致系统起不来。

4.3 签名验证

可信启动的核心,就是验证。GRUB2的签名验证分两层:

  1. GRUB自身验证:由前一级(如shim)验证GRUB的二进制文件和配置文件。
  2. 内核验证:GRUB验证内核、initramfs的签名。

签名验证的配置在 /etc/grub.d/ 里,但更关键的是BIOS/UEFI里的安全启动设置。如果安全启动开启,但GRUB或内核没签名,系统会直接拒绝启动。

我曾经遇到一个案例:客户说服务器更新内核后起不来了。我过去一看,安全启动是开启的,但新内核没签名。嗯,这就是典型的「更新了内核但忘了签名」的坑。

验证签名的命令是:

# 验证内核签名
mokutil --list-enrolled

# 查看当前内核是否签名
pesign -S -i /boot/vmlinuz-$(uname -r)

# 手动签名内核
pesign -s -i /boot/vmlinuz-xxx -o /boot/vmlinuz-xxx.signed -c /etc/pki/xxx/cert.pem

注意,签名用的证书必须提前注册到MOK(Machine Owner Key)列表中。注册方法是在启动时进入MOK管理界面,或者用 mokutil --import 导入。

警告: 千万不要在安全启动开启的情况下,用未签名的内核直接重启。否则你会看到「Verification failed: (0x1A) Security Violation」的错误,然后系统就卡死了。解决办法只能是进BIOS关掉安全启动,或者用已签名的备用内核。

4.4 常见故障场景

好了,理论说完了,咱们看看实际中会遇到哪些问题。我按频率排个序:

故障现象 可能原因 排查方法
启动菜单不显示 GRUB_TIMEOUT=0 或配置文件损坏 启动时按住 Shift 键(BIOS)或 Esc 键(UEFI)强制显示菜单
「File not found」错误 内核或initramfs路径不对 进入GRUB命令行,用 ls 命令查看分区内容,确认文件存在
「Verification failed」 安全启动开启,但内核未签名 检查MOK列表,重新签名内核或关闭安全启动
「No suitable video mode」 GRUB图形模式不兼容 在配置中设置 GRUB_TERMINAL=console 强制文本模式
内核panic 内核参数错误或不兼容 在GRUB菜单按 e 编辑,去掉 rhgb quiet 查看详细错误

这里我重点说一个我踩过的坑:「/boot 分区空间不足」。内核更新时,旧内核不会自动删除,时间长了 /boot 分区就满了。然后你执行 grub2-mkconfig 时,它写不进去新的配置文件,但也不报错。下次重启,GRUB读的还是旧配置,新内核根本找不到。

排查方法很简单:

# 查看 /boot 分区使用情况
df -h /boot

# 清理旧内核(保留最近2个)
package-cleanup --oldkernels --count=2

另一个常见问题是 GRUB 配置文件语法错误。比如你手写了一个自定义菜单项,但少了个花括号,GRUB解析时会直接跳过整个文件。结果就是启动菜单只剩一个「恢复模式」选项。

怎么避免?我个人习惯是:每次改完配置,先用 grub2-script-check /boot/grub2/grub.cfg 检查语法。这个命令不会生成新配置,只是检查语法是否正确。

核心思路: 遇到GRUB启动故障,先别慌。按这个顺序排查:

  1. 确认安全启动状态(BIOS/UEFI设置)
  2. 检查 /boot 分区空间
  3. 用临时编辑方式查看内核参数
  4. 检查签名状态
  5. 查看GRUB配置文件语法

80%的问题都能在这五步里找到答案。

最后,送你一个我常用的GRUB命令行救命技巧:如果系统完全起不来,在GRUB菜单按 c 进入命令行,然后手动引导:

# 查看分区
ls
# 设置根分区(假设是 (hd0,msdos1))
set root=(hd0,msdos1)
# 加载内核
linux /vmlinuz-xxx root=/dev/sda1
# 加载initramfs
initrd /initramfs-xxx.img
# 启动
boot

这个办法能绕过所有配置问题,直接启动系统。等系统起来了,再回头修配置文件。嗯,这招我救过不少次场。