Arch Linux UKI 模式下 GRUB 启动项异常排查与修复
一、背景知识
在排查问题之前,需要先了解 GRUB 启动流程中涉及的几个关键组件。
1.1 GRUB(GRand Unified Bootloader)
GRUB 是 Linux 系统常用的引导加载程序。计算机启动时,UEFI 固件首先加载 GRUB,GRUB 再根据配置文件(grub.cfg)将操作系统内核加载到内存中。
GRUB 的配置文件由 grub-mkconfig 命令生成,它会自动扫描系统中的 /etc/grub.d/ 目录,按文件名中的数字前缀顺序依次执行其中的脚本,将所有脚本的输出合并为最终的 grub.cfg。
1 | /etc/grub.d/ |
重要规则:grub-mkconfig 仅执行具有可执行权限(x)的脚本。通过 chmod -x 可以禁用某个脚本,使其不参与配置生成,而无需删除该文件。
1.2 10_linux 脚本
10_linux 是 GRUB 自带的 Linux 内核检测脚本。它的工作方式是传统模式:
- 在
/boot目录下查找内核文件(vmlinuz-linux) - 查找初始化内存盘文件(
initramfs-linux.img) - 查找 CPU 微码文件(
intel-ucode.img或amd-ucode.img) - 生成如下格式的启动项:
1 | menuentry 'Arch Linux' { |
这种方式要求内核、initramfs、微码作为三个独立文件分别加载。
1.3 15_uki 脚本
15_uki 是 GRUB 2.12 版本新增的脚本,用于支持 UKI 启动模式。它的工作方式与 10_linux 完全不同:
- 不会在
grub-mkconfig执行时生成具体的启动项 - 仅在
grub.cfg中写入一条uki命令 - GRUB 在实际启动时执行该命令,动态扫描 EFI 分区中的 UKI 文件并生成启动项
1 | if [ "$grub_platform" = "efi" ]; then |
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 | menuentry 'Arch Linux' { |
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 | nvme0n1p1 (FAT32)/ |
三、问题现象
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 目录时的行为:
- 找到
vmlinuz-linux→ 写入linux /vmlinuz-linux - 找到
intel-ucode.img→ 写入initrd /intel-ucode.img - 未找到
initramfs-linux.img→ 跳过,不写入
最终生成的启动项缺少 initramfs:
1 | menuentry 'Arch Linux' { |
4.2 缺少 initramfs 的后果
initramfs(初始化内存盘)包含启动早期阶段所需的驱动程序和工具。内核加载后需要通过 initramfs 来挂载根文件系统。缺少 initramfs 时,内核无法访问磁盘控制器驱动或文件系统驱动,导致根文件系统挂载失败,触发内核恐慌(Kernel Panic),表现为蓝屏。
4.3 两个脚本的冲突
10_linux 和 15_uki 各自独立运行,互不感知:
1 | 10_linux 脚本 |
两个脚本不知道对方的存在,也无法判断用户实际使用的是传统模式还是 UKI 模式,因此同时生成了两种启动项。
4.4 为什么蓝屏的选项有图标,正常的反而没有
GRUB 主题通过 menuentry 中的 --class 参数匹配图标。10_linux 生成的条目包含 --class arch,BigSur 主题中存在对应的 arch 图标文件,因此显示图标。
15_uki 在启动时动态生成的条目,其图标匹配逻辑与静态条目不同,可能未正确关联到主题的图标文件,因此不显示图标。
五、错误修复尝试
在排查过程中,以下操作导致了更严重的问题:
5.1 错误操作
- 禁用
15_uki脚本:sudo chmod -x /etc/grub.d/15_uki - 删除 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 | # 挂载根分区和 EFI 分区 |
六、正确修复方案
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 | menuentry 'Arch Linux' --class arch --class gnu-linux --class gnu --class os { |
各字段说明:
| 字段 | 说明 |
|---|---|
--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 | # 生成到临时文件 |
确认输出为 chainloader 方式,且不包含 linux /vmlinuz-linux:
1 | menuentry 'Arch Linux' --class arch --class gnu-linux --class gnu --class os { |
第四步:覆盖生效
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 | sudo grub-mkconfig -o /tmp/grub.cfg.test |
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 | # 检查 UKI 文件时间戳 |
如果 UKI 文件未更新,手动执行:
1 | sudo mkinitcpio -P |
7.2 重新生成 GRUB 配置
由于 10_linux 已被禁用,grub-mkconfig 不会再生成错误的传统启动项,可以安全执行:
1 | sudo grub-mkconfig -o /boot/grub/grub.cfg |
7.3 检查启动配置是否正确
1 | # 确认 10_linux 处于禁用状态 |
八、总结
| 项目 | 内容 |
|---|---|
| 问题根源 | 10_linux 脚本在 UKI 模式下生成了缺少 initramfs 的传统启动项 |
| 直接原因 | 10_linux 无法感知系统的 UKI 模式,按传统方式生成配置 |
| 触发条件 | 系统启用 UKI 模式,且 mkinitcpio 未生成独立的 initramfs-linux.img |
| 修复方法 | 禁用 10_linux,通过 40_custom 手动添加 chainloader 启动项 |
| 关键教训 | GRUB 菜单中有图标不代表正常,无图标不代表异常;修改启动配置前应先理解各脚本的作用 |

