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 配置体系的核心。它干了三件事:
- 读取 /etc/default/grub,加载用户设置的全局变量。
- 按顺序执行 /etc/grub.d/ 下的所有可执行脚本,每个脚本输出一段 GRUB2 配置文本。
- 将所有输出拼接成完整的 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-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 的机制来保证备份的自动化和可靠性。