GRUB2 引导加载器深度解析:配置文件结构、grub.cfg 详解与 grub-mkconfig 原理

说实话,很多运维兄弟对 GRUB2 的态度就是「能开机就行」。但一旦遇到内核升级后起不来、启动参数配错、或者需要回滚内核版本,你就知道 GRUB2 的配置结构有多重要了。我经历过一次线上事故——升级内核后忘记备份 grub.cfg,结果回滚时发现默认启动项指向了不存在的内核,服务器直接起不来。那次之后,我花了一整天把 GRUB2 的配置体系彻底摸了一遍。

这一章,我们就来拆解 GRUB2 的配置文件结构。你想想看,搞懂了它,启动参数备份方案就有了根基。

GRUB2 配置文件体系:三层结构

GRUB2 的配置不像老版 GRUB 那样只有一个 menu.lst。它分了三层:主配置文件、脚本片段目录、用户自定义配置。我习惯把这套结构比作「Nginx 的 include 机制」——模块化、可扩展、好维护。

层级 文件/目录 作用
主配置文件 /boot/grub2/grub.cfg GRUB2 实际读取的启动菜单,由工具自动生成
脚本片段目录 /etc/grub.d/ 存放生成 grub.cfg 的脚本片段,按数字前缀排序执行
用户自定义配置 /etc/default/grub 用户设置的全局变量,如默认启动项、超时时间、内核参数

嗯,这里要注意:永远不要手动编辑 grub.cfg。你改完,下次 grub-mkconfig 一跑,全给你覆盖掉。我见过有人直接在 grub.cfg 里加启动参数,结果内核升级后配置全丢了——这就是没搞懂这三层的关系。

grub.cfg 文件结构详解

我们打开一个典型的 grub.cfg 看看。它其实是一个 GRUB2 脚本,语法类似 Bash,但有自己的指令集。

#
# DO NOT EDIT THIS FILE
#
# It is automatically generated by grub-mkconfig using templates
# from /etc/grub.d and settings from /etc/default/grub
#

### BEGIN /etc/grub.d/00_header ###
set default="${saved_entry}"
if [ x"${feature_menuentry_id}" = xy ]; then
  menuentry_id_option="--id"
else
  menuentry_id_option=""
fi
export menuentry_id_option
### END /etc/grub.d/00_header ###

### BEGIN /etc/grub.d/10_linux ###
menuentry 'CentOS Linux (5.10.0-60.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-5.10.0-60.el7.x86_64-advanced-xxx' {
  load_video
  set gfxpayload=keep
  insmod gzio
  insmod part_msdos
  insmod ext2
  set root='hd0,msdos1'
  if [ x$feature_platform_search_hint = xy ]; then
    search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  else
    search --no-floppy --fs-uuid --set=root xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  fi
  linux16 /vmlinuz-5.10.0-60.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
  initrd16 /initramfs-5.10.0-60.el7.x86_64.img
}
### END /etc/grub.d/10_linux ###

### BEGIN /etc/grub.d/30_os-prober ###
### END /etc/grub.d/30_os-prober ###

### BEGIN /etc/grub.d/40_custom ###
if [ -f  ${config_directory}/custom.cfg ]; then
  source ${config_directory}/custom.cfg
elif [ -z "${config_directory}" -a -f  /boot/grub2/custom.cfg ]; then
  source /boot/grub2/custom.cfg
fi
### END /etc/grub.d/40_custom ###

看到没?每个区块都标注了来源。我当年排查一个问题——grub.cfg 里多了一个奇怪的内核参数,查了半天,最后发现是 /etc/grub.d/10_linux 里某个脚本片段搞的鬼。所以,学会看区块注释,这是定位问题的第一把钥匙。

核心要点:grub.cfg 中的 menuentry 块定义了每个启动项。其中 linux16 行指定内核文件路径和启动参数,initrd16 行指定初始 RAM 磁盘。这两个字段,就是我们在备份方案中需要重点关注和保护的。

/etc/default/grub:你的配置入口

这个文件才是你真正该动的地方。它定义了 GRUB2 的全局行为,语法就是简单的 KEY=VALUE。

GRUB_TIMEOUT=5
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"
GRUB_DISABLE_RECOVERY="true"

我个人最常用的几个参数:

  • GRUB_DEFAULT:设置默认启动项。设为 saved 表示记住上次选择的项,设为数字则固定启动第几个菜单项。
  • GRUB_TIMEOUT:菜单等待时间,单位秒。设为 -1 则无限等待——我建议在维护窗口期这么干,防止超时自动进入错误内核。
  • GRUB_CMDLINE_LINUX:这是重点!所有内核启动参数都写在这里。比如加 single 进单用户模式,或者加 systemd.unit=rescue.target 进救援模式。

避坑指南:我曾经在 GRUB_CMDLINE_LINUX 里加了一个错误的参数,导致内核 panic。当时没备份,只能进救援模式手动改。后来我养成了一个习惯——每次修改前先 cp /etc/default/grub /etc/default/grub.bak.$(date +%Y%m%d)。这个习惯救过我两次。

/etc/grub.d/ 目录:配置生成引擎

这个目录下的脚本决定了 grub.cfg 最终长什么样。脚本按数字前缀排序执行:

脚本 功能
00_header 生成头部信息,设置默认项、超时、主题等
10_linux 扫描 /boot 目录下的内核,生成 Linux 启动菜单项
20_linux_xen Xen 虚拟化相关(如果有)
30_os-prober 探测其他操作系统(如 Windows),生成双启动项
40_custom 用户自定义菜单项入口,可以在这里添加自己的启动项
41_custom 类似 40_custom,通常用于加载 custom.cfg

你想想看,如果我们想添加一个「带备份内核参数」的启动项,最干净的做法就是在 /etc/grub.d/40_custom 里写一个自定义 menuentry。这样既不影响自动生成的部分,又能保证我们的备份方案独立存在。

grub-mkconfig 原理:配置生成流水线

这个命令是 GRUB2 配置体系的核心。它干了三件事:

  1. 读取 /etc/default/grub,加载用户设置的全局变量。
  2. 按顺序执行 /etc/grub.d/ 下的所有可执行脚本,每个脚本输出一段 GRUB2 配置文本。
  3. 将所有输出拼接成完整的 grub.cfg,写入 /boot/grub2/ 目录。

说白了,grub-mkconfig 就是一个「配置生成器」。它的输入是模板脚本和变量文件,输出是最终的启动菜单。

# 手动生成配置的命令
grub2-mkconfig -o /boot/grub2/grub.cfg

# 或者在某些发行版上
grub-mkconfig -o /boot/grub/grub.cfg

重要提醒:每次修改 /etc/default/grub 或 /etc/grub.d/ 下的脚本后,必须执行 grub-mkconfig 才能生效。我见过有人改完配置重启,发现没变化——就是因为忘了跑这个命令。另外,建议在跑之前先备份当前的 grub.cfg:cp /boot/grub2/grub.cfg /boot/grub2/grub.cfg.bak。万一新配置有问题,还能手动恢复。

知识体系总览

下面这张图把 GRUB2 的配置流程和文件关系串起来了。我建议你把它存下来,排查问题时对着看,思路会清晰很多。

GRUB2 配置体系与生成流程 /etc/default/grub /etc/grub.d/ 脚本目录 grub-mkconfig 读取变量 + 执行脚本 /boot/grub2/grub.cfg GRUB2 引导加载器读取 💡 关键原则: 修改 /etc/default/grub 或 /etc/grub.d/ 脚本 → 执行 grub-mkconfig → 生成 grub.cfg → 重启生效 永远不要直接编辑 grub.cfg,除非在紧急恢复场景下

实战中的配置检查技巧

最后分享几个我常用的检查命令:

  • grub2-editenv list:查看当前 saved_entry 的值,确认默认启动项是哪个。
  • ls -l /etc/grub.d/:检查脚本的执行权限,没有 x 权限的脚本会被跳过。
  • grep -E '^GRUB_CMDLINE_LINUX' /etc/default/grub:快速查看当前内核参数配置。
  • diff /boot/grub2/grub.cfg /boot/grub2/grub.cfg.bak:对比新老配置的差异,确认改动符合预期。

嗯,这一章的内容就到这里。GRUB2 的配置体系搞清楚了,后面讲启动参数备份方案时,你就能理解为什么我们要在 /etc/grub.d/40_custom 里动手脚,以及如何通过 grub-mkconfig 的机制来保证备份的自动化和可靠性。