GRUB2 引导管理器完全教程 / 第 4 章:自动生成配置
第 4 章:自动生成配置
4.1 grub-mkconfig 概述
grub-mkconfig 是 GRUB2 的配置生成工具,它扫描系统中已安装的内核和其他操作系统,自动生成 /boot/grub/grub.cfg 文件。在 Debian/Ubuntu 系统中,update-grub 是 grub-mkconfig 的一个封装脚本。
4.1.1 基本用法
# Debian/Ubuntu(封装命令)
$ sudo update-grub
# 等效于
$ sudo grub-mkconfig -o /boot/grub/grub.cfg
# RHEL/CentOS/Fedora
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# Arch Linux
$ sudo grub-mkconfig -o /boot/grub/grub.cfg
4.1.2 查看输出而不写入
# 将生成的配置输出到标准输出(不写文件)
$ sudo grub-mkconfig
这在调试时非常有用,可以检查生成的配置是否正确,而不影响当前的 grub.cfg。
4.1.3 工作流程
grub-mkconfig 执行流程:
1. 加载 /etc/default/grub 中的变量
2. 运行 /etc/grub.d/ 下所有可执行脚本(按文件名排序)
3. 各脚本输出菜单项片段到 stdout
4. 所有输出合并写入 grub.cfg
/etc/default/grub ← 用户配置变量
│
▼
┌──────────────────────────────┐
│ /etc/grub.d/ │
│ ├── 00_header ───────────── 超时、默认项、主题
│ ├── 05_debian_theme ──────────── 背景、颜色
│ ├── 10_linux ───────────── Linux 内核
│ ├── 20_linux_xen ───────────── Xen 虚拟化
│ ├── 30_os-prober ───────────── 其他操作系统
│ ├── 30_uefi-firmware ─────────── UEFI 固件入口
│ ├── 40_custom ───────────── 用户自定义菜单
│ └── 41_custom ───────────── 额外自定义配置
└──────────────────────────────┘
│
▼
/boot/grub/grub.cfg ← 生成的最终配置
4.2 /etc/grub.d/ 脚本详解
4.2.1 脚本命名规则
脚本按文件名的字母顺序执行。命名约定:
| 前缀 | 含义 |
|---|---|
| 00-09 | GRUB 头部设置 |
| 10-19 | 主要操作系统条目 |
| 20-29 | 辅助操作系统条目 |
| 30-39 | 其他操作系统探测 |
| 40-49 | 用户自定义 |
| 90-99 | 尾部(如 90_persistent) |
4.2.2 控制脚本是否执行
# 方法一:移除可执行权限
$ sudo chmod -x /etc/grub.d/30_os-prober # 禁用 os-prober
# 方法二:添加 .disabled 后缀
$ sudo mv /etc/grub.d/30_os-prober /etc/grub.d/30_os-prober.disabled
# 恢复
$ sudo chmod +x /etc/grub.d/30_os-prober
4.2.3 00_header — 头部配置
00_header 脚本负责读取 /etc/default/grub 中的变量,生成 grub.cfg 的头部:
# 生成的内容包括:
# - set default=...
# - set timeout=...
# - set timeout_style=...
# - loadfont ...
# - insmod ...
# - set gfxmode=...
# - terminal_output gfxterm
# - 主题设置
# - background_image
4.2.4 10_linux — Linux 内核检测
10_linux 脚本是核心脚本,负责检测 /boot 目录中的内核文件并生成菜单项。
检测逻辑:
# 扫描以下目录中的内核文件
/boot/vmlinuz-*
/boot/vmlinuz
/boot/kernel-*
/boot/bzImage-*
# 匹配的 initramfs 文件
/boot/initrd.img-*
/boot/initramfs-*.img
/boot/initramfs-*
生成的菜单项:
# 正常启动项
menuentry 'Debian GNU/Linux' --class debian --class gnu-linux --class gnu --class os {
...
linux /boot/vmlinuz-6.1.0-amd64 root=UUID=xxx ro quiet
initrd /boot/initrd.img-6.1.0-amd64
}
# 恢复模式项(如果未禁用)
menuentry 'Debian GNU/Linux (recovery mode)' --class debian {
...
linux /boot/vmlinuz-6.1.0-amd64 root=UUID=xxx ro single
initrd /boot/initrd.img-6.1.0-amd64
}
4.2.5 20_linux_xen — Xen 虚拟化
检测 Xen hypervisor 并生成对应的菜单项。大多数桌面用户不需要此脚本。
# 如果不使用 Xen,可以禁用
$ sudo chmod -x /etc/grub.d/20_linux_xen
4.2.6 30_os-prober — 检测其他操作系统
30_os-prober 脚本调用 os-prober 工具检测磁盘上安装的其他操作系统。
支持检测的系统:
| 系统 | 检测方式 |
|---|---|
| Windows | 检测 bootmgr / BCD |
| 其他 Linux | 检测内核文件和 initramfs |
| macOS | 检测 HFS+ 分区 |
| BSD | 检测引导加载程序 |
启用 os-prober:
# 1. 安装 os-prober
$ sudo apt install os-prober # Debian/Ubuntu
$ sudo dnf install os-prober # RHEL/Fedora
# 2. 确保 /etc/default/grub 中未禁用
# GRUB_DISABLE_OS_PROBER=false(默认值)
# 3. 重新生成配置
$ sudo update-grub
⚠️ 注意:从 Ubuntu 22.04 和 Debian 12 开始,
GRUB_DISABLE_OS_PROBER默认为true(出于安全考虑),需要手动设为false并安装os-prober。
os-prober 安全提示:
os-prober 需要挂载其他分区来检测操作系统,这在某些安全敏感场景下可能存在风险。如果系统不需要多系统引导,建议保持禁用。
4.2.7 30_uefi-firmware — UEFI 固件入口
在 UEFI 系统上生成一个菜单项,允许直接进入 UEFI 固件设置界面:
menuentry 'System setup' --unrestricted {
fwsetup
}
4.2.8 40_custom — 用户自定义
40_custom 是用户添加自定义菜单项的标准位置。该文件不会被 update-grub 覆盖。
#!/bin/sh
exec tail -n +3 $0
# 该行以上的内容不会出现在 grub.cfg 中
# 自定义菜单项示例:引导 Windows
menuentry "Windows 11" --class windows --class os {
insmod part_gpt
insmod fat
search --no-floppy --fs-uuid --set=root XXXX-XXXX
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
# 自定义菜单项:旧内核
menuentry "Linux 5.10 (旧内核)" {
insmod part_gpt
insmod ext2
set root='hd0,gpt2'
linux /boot/vmlinuz-5.10.0-amd64 root=UUID=xxx ro
initrd /boot/initrd.img-5.10.0-amd64
}
⚠️ 注意:
exec tail -n +3 $0这一行的作用是跳过脚本本身的前 3 行(#!/bin/sh、exec tail、和空行),直接输出后续内容到 grub.cfg。
4.2.9 41_custom — 补充自定义配置
41_custom 加载 /boot/grub/custom.cfg 文件(如果存在),适合在不修改 40_custom 的情况下添加额外菜单:
#!/bin/sh
echo "source /boot/grub/custom.cfg"
然后创建 /boot/grub/custom.cfg:
# /boot/grub/custom.cfg
menuentry "Rescue System" {
insmod part_gpt
insmod ext2
set root='hd0,gpt2'
linux /boot/rescue/vmlinuz root=UUID=xxx
initrd /boot/rescue/initrd.img
}
4.3 os-prober 工具详解
4.3.1 os-prober 工作原理
# 手动运行 os-prober
$ sudo os-prober
# /dev/sda1:Windows 10:Windows:chain
# /dev/sda5:Ubuntu 22.04 LTS (22.04):Ubuntu:linux
探测流程:
- 扫描所有已挂载的分区
- 检查分区上的特征文件(如
/bootmgr、/vmlinuz) - 识别操作系统类型和版本
- 输出设备:名称:类型:引导方式
4.3.2 自定义 os-prober 排除
# 在 /etc/default/grub 中添加排除分区
# GRUB_OS_PROBER_SKIP_LIST="sda1@/dev/sda"
# 或者通过 /etc/os-prober.conf 配置(如果存在)
4.3.3 linux-boot-prober
os-prober 对 Linux 系统的探测依赖 linux-boot-prober,它需要能挂载目标分区并读取内核文件。
# 手动探测特定分区
$ sudo linux-boot-prober /dev/sda5
# /dev/sda5:/boot/vmlinuz-5.15.0:/boot/initrd.img-5.15.0:root=UUID=xxx:ro:
4.4 变量与环境
4.4.1 grub-mkconfig 使用的变量
grub-mkconfig 在执行时会设置以下内部变量:
| 变量 | 说明 | 来源 |
|---|---|---|
GRUB_DEFAULT | 默认启动项 | /etc/default/grub |
GRUB_TIMEOUT | 超时时间 | /etc/default/grub |
GRUB_CMDLINE_LINUX_DEFAULT | 默认内核参数 | /etc/default/grub |
GRUB_CMDLINE_LINUX | 所有菜单项的内核参数 | /etc/default/grub |
GRUB_TERMINAL | 终端类型 | /etc/default/grub |
GRUB_THEME | 主题路径 | /etc/default/grub |
GRUB_GFXMODE | 图形分辨率 | /etc/default/grub |
4.4.2 传递额外参数给内核
# 在 /etc/default/grub 中
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX="ipv6.disable=1"
# 效果:所有菜单项的内核参数会包含 ipv6.disable=1
# 正常模式:quiet splash ipv6.disable=1
# 恢复模式:single ipv6.disable=1
4.4.3 按菜单项添加参数
如果需要为特定内核添加参数,可以在 /etc/grub.d/10_linux 中添加逻辑,或使用 40_custom 手动定义菜单项。
4.5 高级用法
4.5.1 自定义 10_linux 脚本
在某些场景下,你可能需要修改 10_linux 来改变内核检测逻辑。
示例:在所有内核菜单项中添加额外参数
# 编辑 /etc/grub.d/10_linux
# 找到以下行(大约在第 180-200 行附近):
# "${GRUB_CMDLINE_LINUX@Q}" "${GRUB_CMDLINE_LINUX_DEFAULT@Q}"
# 可以在函数调用处添加额外参数
# 更好的做法:使用 GRUB_CMDLINE_LINUX
# 在 /etc/default/grub 中:
GRUB_CMDLINE_LINUX="apparmor=1 security=apparmor"
4.5.2 禁用恢复模式菜单项
# 在 /etc/default/grub 中
GRUB_DISABLE_RECOVERY="true"
# 然后重新生成
$ sudo update-grub
4.5.3 禁用子菜单
默认情况下,多个内核版本会被组织到子菜单中:
# 禁用子菜单,所有项平铺显示
GRUB_DISABLE_SUBMENU=true
4.5.4 限制显示的内核数量
方法一:手动移除旧内核
# 查看已安装的内核
$ dpkg --list | grep linux-image
# 移除旧内核(保留当前和一个备用)
$ sudo apt remove linux-image-5.10.0-amd64
$ sudo update-grub
方法二:使用 apt autoremove
$ sudo apt autoremove --purge
方法三:配置 apt 自动清理
# /etc/apt/apt.conf.d/99-autoremove-kernels
# 控制保留的内核数量
4.5.5 预生成配置检查
# 先查看生成的配置内容
$ sudo grub-mkconfig 2>&1 | head -100
# 检查是否有错误
$ sudo grub-mkconfig 2>/tmp/grub-errors.log
$ cat /tmp/grub-errors.log
4.6 发行版差异对照
| 项目 | Debian/Ubuntu | RHEL/Fedora | Arch Linux |
|---|---|---|---|
| 命令 | update-grub | grub2-mkconfig | grub-mkconfig |
| 配置文件 | /etc/default/grub | /etc/default/grub | /etc/default/grub |
| 输出路径 | /boot/grub/grub.cfg | /boot/grub2/grub.cfg | /boot/grub/grub.cfg |
| 脚本目录 | /etc/grub.d/ | /etc/grub.d/ | /etc/grub.d/ |
| 环境文件 | /boot/grub/grubenv | /boot/grub2/grubenv | /boot/grub/grubenv |
| 持久化默认项 | GRUB_SAVEDEFAULT=true | 需配置 grubby | GRUB_SAVEDEFAULT=true |
RHEL/Fedora 特殊配置
# RHEL 使用 grubby 管理内核
# 查看默认内核
$ grubby --default-kernel
# /boot/vmlinuz-5.14.0-162.6.1.el9_1.x86_64
# 设置默认内核
$ sudo grubby --set-default /boot/vmlinuz-5.14.0-162.6.1.el9_1.x86_64
# 修改内核参数
$ sudo grubby --update-kernel=ALL --args="ipv6.disable=1"
4.7 调试 grub-mkconfig
4.7.1 常见问题诊断
问题:os-prober 未检测到 Windows
# 1. 确认 os-prober 已安装
$ which os-prober
# 2. 确认 Windows 分区已挂载(os-prober 需要)
$ sudo mount /dev/sda1 /mnt
$ sudo os-prober
# 3. 确认未禁用
$ grep DISABLE_OS_PROBER /etc/default/grub
GRUB_DISABLE_OS_PROBER=false
# 4. 重新生成
$ sudo update-grub
问题:未检测到新安装的内核
# 1. 确认内核文件存在于 /boot
$ ls /boot/vmlinuz-*
# 2. 确认 initramfs 已生成
$ ls /boot/initrd.img-* /boot/initramfs-*
# 3. 如果缺少 initramfs,手动生成
$ sudo update-initramfs -c -k 6.1.0-amd64 # Debian/Ubuntu
$ sudo dracut --force /boot/initramfs-6.1.0.img 6.1.0 # RHEL/Fedora
# 4. 重新生成 grub.cfg
$ sudo update-grub
4.7.2 详细输出
# grub-mkconfig 默认会输出进度信息到 stderr
$ sudo grub-mkconfig -o /boot/grub/grub.cfg 2>&1
# Generating grub configuration file ...
# Found linux image: /boot/vmlinuz-6.1.0-amd64
# Found initrd image: /boot/initrd.img-6.1.0-amd64
# Found Windows Boot Manager on /dev/sda1@/EFI/Microsoft/Boot/bootmgfw.efi
# done
4.8 扩展阅读
上一章:第 3 章:配置文件详解 | 下一章:第 5 章:自定义菜单项