一、背景知识

在排查问题之前,需要先了解 GRUB 启动流程中涉及的几个关键组件。

1.1 GRUB(GRand Unified Bootloader)

GRUB 是 Linux 系统常用的引导加载程序。计算机启动时,UEFI 固件首先加载 GRUB,GRUB 再根据配置文件(grub.cfg)将操作系统内核加载到内存中。

GRUB 的配置文件由 grub-mkconfig 命令生成,它会自动扫描系统中的 /etc/grub.d/ 目录,按文件名中的数字前缀顺序依次执行其中的脚本,将所有脚本的输出合并为最终的 grub.cfg

1
2
3
4
5
6
7
8
9
/etc/grub.d/
├── 00_header # 基础设置(主题、超时时间、字体等)
├── 10_linux # 检测 Linux 内核,生成传统启动项
├── 15_uki # 检测 UKI 文件,生成 UKI 启动项
├── 20_linux_xen # 检测 Xen 虚拟化内核
├── 30_os-prober # 检测其他操作系统(如 Windows)
├── 30_uefi-firmware # UEFI 固件设置入口
├── 40_custom # 用户自定义启动项
└── 41_custom # 加载自定义配置文件

重要规则grub-mkconfig 仅执行具有可执行权限(x)的脚本。通过 chmod -x 可以禁用某个脚本,使其不参与配置生成,而无需删除该文件。

1.2 10_linux 脚本

10_linux 是 GRUB 自带的 Linux 内核检测脚本。它的工作方式是传统模式

  • /boot 目录下查找内核文件(vmlinuz-linux
  • 查找初始化内存盘文件(initramfs-linux.img
  • 查找 CPU 微码文件(intel-ucode.imgamd-ucode.img
  • 生成如下格式的启动项:
1
2
3
4
menuentry 'Arch Linux' {
linux /vmlinuz-linux root=/dev/nvme0n1p6 rw quiet
initrd /intel-ucode.img /initramfs-linux.img
}

这种方式要求内核、initramfs、微码作为三个独立文件分别加载。

1.3 15_uki 脚本

15_uki 是 GRUB 2.12 版本新增的脚本,用于支持 UKI 启动模式。它的工作方式与 10_linux 完全不同:

  • 不会在 grub-mkconfig 执行时生成具体的启动项
  • 仅在 grub.cfg 中写入一条 uki 命令
  • GRUB 在实际启动时执行该命令,动态扫描 EFI 分区中的 UKI 文件并生成启动项
1
2
3
if [ "$grub_platform" = "efi" ]; then
uki
fi

1.4 UKI(Unified Kernel Image,统一内核镜像)

UKI 是一种将多个启动组件打包为单个 EFI 可执行文件的技术。一个 UKI 文件包含:

组件 说明
Linux 内核 操作系统核心
initramfs 初始化内存盘,包含启动早期所需的驱动和工具
CPU 微码 Intel/AMD 处理器的微码更新
内核命令行参数 root=quiet 等启动参数

在 Arch Linux 中,UKI 文件由 mkinitcpio 生成,默认存放路径为:

1
/boot/EFI/Linux/arch-linux.efi

UKI 模式的启动项使用 chainloader 方式加载,只需一行命令:

1
2
3
menuentry 'Arch Linux' {
chainloader /EFI/Linux/arch-linux.efi
}

1.5 40_custom 脚本

40_custom 是 GRUB 提供的用户自定义启动项文件。用户可以在其中手动编写 menuentry,这些自定义条目会在 grub-mkconfig 生成配置时被原样包含。

由于 40_custom 的内容不会被自动覆盖或修改,它是实现持久化自定义启动项的推荐方式。

1.6 传统模式与 UKI 模式的对比

对比项 传统模式(10_linux) UKI 模式
文件结构 内核、initramfs、微码分开存放 打包为单个 .efi 文件
GRUB 加载方式 linux + initrd 分步加载 chainloader 一步加载
生成工具 grub-mkconfig 自动检测 mkinitcpio 生成,15_uki 检测
配置文件 /etc/grub.d/10_linux /etc/grub.d/15_uki
mkinitcpio preset default_image="/boot/initramfs-linux.img" default_uki="/boot/EFI/Linux/arch-linux.efi"

二、系统环境

本次问题涉及的硬件与软件环境如下:

2.1 分区布局

设备 分区 文件系统 用途
nvme0n1 p1 FAT32 EFI 系统分区(GRUB + Windows 共享)
nvme0n1 p2 NTFS Windows 系统分区
nvme0n1 p5 ext4 Arch Linux 备用分区
nvme0n1 p6 ext4 Arch Linux 根分区(/

其中 /boot 挂载到 EFI 分区(nvme0n1p1),这意味着内核文件、initramfs、UKI 文件都存放在 EFI 分区上。

2.2 软件配置

组件 版本/说明
操作系统 Arch Linux + Windows 双系统
引导程序 GRUB2(x86_64-efi)
GRUB 主题 BigSur(Teraskull/bigsur-grub2-theme)
内核启动模式 UKI
mkinitcpio preset 同时生成 initramfs 和 UKI

2.3 EFI 分区文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
nvme0n1p1 (FAT32)/
├── EFI/
│ ├── Boot/bootx64.efi
│ ├── GRUB/grubx64.efi
│ ├── Linux/
│ │ └── arch-linux.efi ← UKI 文件
│ └── Microsoft/Boot/bootmgfw.efi ← Windows Boot Manager
├── grub/
│ ├── grub.cfg ← GRUB 配置
│ ├── themes/bigsur/ ← BigSur 主题
│ └── x86_64-efi/ ← GRUB 模块
├── vmlinuz-linux ← 内核镜像
├── intel-ucode.img ← CPU 微码
└── initramfs-linux.img ← 初始化内存盘(可能不存在)

三、问题现象

3.1 初始状态

安装 GRUB 并执行 grub-mkconfig 后,重启进入 GRUB 菜单,出现以下 5 个启动选项:

序号 选项名称 图标 启动结果
1 Arch Linux 有(BigSur 主题 arch 图标) 蓝屏(内核恐慌)
2 Arch Linux 高级选项 子菜单
3 Arch Linux(UKI 动态生成) 正常进入系统
4 Windows Boot Manager 正常
5 UEFI Firmware Settings 正常

3.2 问题表现

  • 第 1 个选项(有图标):选择后出现蓝屏/内核恐慌,系统无法启动
  • 第 3 个选项(无图标):可以正常启动并进入 Arch Linux 桌面
  • 两个 Arch Linux 选项共存,用户需要手动选择第二个才能正常启动

四、原因分析

4.1 根本原因:10_linux 在 UKI 模式下生成了不完整的启动项

10_linux 脚本使用传统方式生成启动项,要求系统中存在独立的 initramfs-linux.img 文件。但在 UKI 模式下,mkinitcpio 的 preset 配置可能仅生成 UKI 文件,不生成独立的 initramfs-linux.img

10_linux 在扫描 /boot 目录时的行为:

  1. 找到 vmlinuz-linux → 写入 linux /vmlinuz-linux
  2. 找到 intel-ucode.img → 写入 initrd /intel-ucode.img
  3. 未找到 initramfs-linux.img跳过,不写入

最终生成的启动项缺少 initramfs:

1
2
3
4
menuentry 'Arch Linux' {
linux /vmlinuz-linux root=/dev/nvme0n1p6 rw quiet
initrd /intel-ucode.img # 仅有微码,缺少 initramfs-linux.img
}

4.2 缺少 initramfs 的后果

initramfs(初始化内存盘)包含启动早期阶段所需的驱动程序和工具。内核加载后需要通过 initramfs 来挂载根文件系统。缺少 initramfs 时,内核无法访问磁盘控制器驱动或文件系统驱动,导致根文件系统挂载失败,触发内核恐慌(Kernel Panic),表现为蓝屏。

4.3 两个脚本的冲突

10_linux15_uki 各自独立运行,互不感知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
10_linux 脚本

检测到 vmlinuz-linux 和 intel-ucode.img

生成传统启动项(linux + initrd)

缺少 initramfs → 蓝屏

15_uki 脚本

检测到 /EFI/Linux/arch-linux.efi

生成 UKI 启动项(chainloader)

包含所有组件 → 正常启动

两个脚本不知道对方的存在,也无法判断用户实际使用的是传统模式还是 UKI 模式,因此同时生成了两种启动项。

4.4 为什么蓝屏的选项有图标,正常的反而没有

GRUB 主题通过 menuentry 中的 --class 参数匹配图标。10_linux 生成的条目包含 --class arch,BigSur 主题中存在对应的 arch 图标文件,因此显示图标。

15_uki 在启动时动态生成的条目,其图标匹配逻辑与静态条目不同,可能未正确关联到主题的图标文件,因此不显示图标。


五、错误修复尝试

在排查过程中,以下操作导致了更严重的问题:

5.1 错误操作

  1. 禁用 15_uki 脚本:sudo chmod -x /etc/grub.d/15_uki
  2. 删除 UKI 文件:sudo rm /boot/EFI/Linux/arch-linux.efi

5.2 错误原因

上述操作基于错误判断——将”有图标的选项”视为正常选项,将”无图标的选项”视为异常选项。实际上恰恰相反:

选项 来源 实际状态
有图标 10_linux(传统方式) 蓝屏
无图标 15_uki(UKI 方式) 正常

禁用 15_uki 并删除 UKI 文件后,系统中仅保留 10_linux 生成的蓝屏启动项。重启后 GRUB 菜单只剩一个 Arch Linux 选项,选择后必然蓝屏,系统完全无法启动。

5.3 恢复方法

需要通过 Arch Linux Live USB 启动,挂载根分区,重新生成 UKI 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 挂载根分区和 EFI 分区
mount /dev/nvme0n1p6 /mnt
mount /dev/nvme0n1p1 /mnt/boot

# 进入 chroot 环境
arch-chroot /mnt

# 重新生成 UKI 文件
mkinitcpio -P

# 退出并重启
exit
umount -R /mnt
reboot

六、正确修复方案

6.1 修复思路

  • 禁用 10_linux,阻止它生成有问题的传统启动项
  • 40_custom 中手动编写正确的 chainloader 启动项
  • 保留 15_uki 作为备用(或禁用以避免重复条目)
  • 手动编写的启动项包含 --class arch,确保主题图标正确显示

6.2 修复步骤

第一步:禁用 10_linux 脚本

1
sudo chmod -x /etc/grub.d/10_linux

此操作不会删除文件,仅取消其可执行权限。grub-mkconfig 在执行时会跳过没有可执行权限的脚本。

第二步:编辑 40_custom 添加自定义启动项

1
sudo nvim /etc/grub.d/40_custom

在文件末尾(注释行之后)添加以下内容:

1
2
3
4
5
6
7
menuentry 'Arch Linux' --class arch --class gnu-linux --class gnu --class os {
insmod part_gpt
insmod fat
insmod chain
search --no-floppy --fs-uuid --set=root BCD2-2326
chainloader /EFI/Linux/arch-linux.efi
}

各字段说明:

字段 说明
--class arch 主题图标匹配标识,使 BigSur 主题显示 Arch 图标
insmod part_gpt 加载 GPT 分区表支持模块
insmod fat 加载 FAT 文件系统支持模块
insmod chain 加载 chainloader 支持模块
search ... BCD2-2326 通过 UUID 定位 EFI 分区
chainloader /EFI/Linux/arch-linux.efi 加载 UKI 文件

注意:BCD2-2326 为 EFI 分区的 UUID,需要替换为实际值。可通过 blkid /dev/nvme0n1p1 查看。

第三步:生成新配置并验证

建议先输出到临时文件,确认内容正确后再覆盖:

1
2
3
4
5
# 生成到临时文件
sudo grub-mkconfig -o /tmp/grub.cfg.test

# 检查启动项内容
sudo grep -A 6 "menuentry 'Arch Linux'" /tmp/grub.cfg.test

确认输出为 chainloader 方式,且不包含 linux /vmlinuz-linux

1
2
3
4
5
6
7
menuentry 'Arch Linux' --class arch --class gnu-linux --class gnu --class os {
insmod part_gpt
insmod fat
insmod chain
search --no-floppy --fs-uuid --set=root BCD2-2326
chainloader /EFI/Linux/arch-linux.efi
}

第四步:覆盖生效

1
sudo cp /tmp/grub.cfg.test /boot/grub/grub.cfg

第五步:(可选)禁用 15_uki 避免重复条目

如果 15_uki 在启动时动态生成的 UKI 条目与 40_custom 的条目重复,可以一并禁用:

1
sudo chmod -x /etc/grub.d/15_uki

禁用后重新生成配置:

1
2
sudo grub-mkconfig -o /tmp/grub.cfg.test
sudo cp /tmp/grub.cfg.test /boot/grub/grub.cfg

6.3 修复后的 GRUB 菜单

序号 选项名称 图标 来源 启动结果
1 Arch Linux 40_custom(chainloader UKI) 正常
2 Windows Boot Manager 30_os-prober 正常
3 UEFI Firmware Settings 30_uefi-firmware 正常

七、日常维护

7.1 内核更新后

内核更新时,mkinitcpio 会自动重新生成 UKI 文件。无需额外操作,但建议确认 UKI 文件已正确更新:

1
2
# 检查 UKI 文件时间戳
ls -la /boot/EFI/Linux/arch-linux.efi

如果 UKI 文件未更新,手动执行:

1
sudo mkinitcpio -P

7.2 重新生成 GRUB 配置

由于 10_linux 已被禁用,grub-mkconfig 不会再生成错误的传统启动项,可以安全执行:

1
sudo grub-mkconfig -o /boot/grub/grub.cfg

7.3 检查启动配置是否正确

1
2
3
4
5
6
7
8
9
10
# 确认 10_linux 处于禁用状态
ls -l /etc/grub.d/10_linux
# 输出应无 x 权限:-rw-r--r--

# 确认 40_custom 内容正确
cat /etc/grub.d/40_custom

# 确认 GRUB 配置中无传统启动方式
grep "linux /vmlinuz" /boot/grub/grub.cfg
# 应无输出

八、总结

项目 内容
问题根源 10_linux 脚本在 UKI 模式下生成了缺少 initramfs 的传统启动项
直接原因 10_linux 无法感知系统的 UKI 模式,按传统方式生成配置
触发条件 系统启用 UKI 模式,且 mkinitcpio 未生成独立的 initramfs-linux.img
修复方法 禁用 10_linux,通过 40_custom 手动添加 chainloader 启动项
关键教训 GRUB 菜单中有图标不代表正常,无图标不代表异常;修改启动配置前应先理解各脚本的作用