UEFI 启动背后的原理
适用场景:在双系统、移动硬盘装系统时遇到启动项丢失、引导装错分区等「玄学」问题。你会学到 UEFI 启动的真实逻辑(NVRAM 启动项 + EFI 文件)、SecureBoot 的真相,以及系统安装时引导器无视选择装进第一个 EFI 分区的天坑与补救方法。
现在的电脑基本上都是采取 UEFI 启动了,之前看过一些资料,大抵都讲道"现在 UEFI 不需要制作引导分区了,系统会自动从 EFI 分区下的 \EFI\Boot\Bootx64.efi 文件启动",这种说法其实是以偏概全了。导致很多情况下出现了一些奇怪的问题(比如当我准备把操作系统安装到一个移动硬盘上的时候)。
究其错误的原因,从根本上来说是忽略了主板上 NVRAM(非易失性随机访问存储器,主板上自带的一小块配置存储器,不在硬盘上,你把整块硬盘都擦了它还在)中启动配置的存在。
真正的 UEFI 启动逻辑
def 从EFI文件启动(文件路径):
根据文件路径加载EFI文件
if 开启了SecureBoot功能:
if not SecureBoot校验通过:
启动失败报错退出 # 一般可以在屏幕上看到错误信息
执行EFI # 一般来说也就从这里进入了操作系统或者Grub,所以不会返回
def 从存储设备启动(存储设备):
# 可以是是GPT分区,也可以是MBR分区
for 分区 in 存储设备:
# 分区类型在MBR/GPT的分区表中标注,其本质是个普通FAT文件系统分区
if 分区类型 == EFI系统分区:
# 注意,FAT文件系统路径不区分大小写,且由于出自Windows,所以其路径习惯用反斜杠\而非斜杠/
# 对于非x86_64类型的硬件,默认路径也有差异
if 分区根目录下存在"\EFI\Boot\Bootx64.efi"文件:
从EFI文件启动(分区索引 + 该文件在分区下路径)
def 从EFI启动项启动(启动项):
if 该启动项指向的是一个EFI文件路径:
从EFI文件启动(启动项所指文件路径)
elif 启动项指向的是一个存储设备:
从存储设备启动(启动项所指设备)
def 启动():
根据主板NVRAM中启动项配置,以及扫描的硬件(比如刚插入装机U盘等),生成启动顺序表
if 用户无操控:
for 启动项 in 启动顺序表:
从EFI启动项启动(启动项) # 成功则返回,失败可能会返回,也可能是报错退出
else:
根据启动顺序表选择界面
if 用户选择了一个启动项:
# 这对应了手动从列表中选择一个启动设备
从EFI启动项启动(启动项)
elif 用户手动选择了一个具体的EFI文件:
# 这对应了手动浏览EFI分区并选择某一个具体的文件
从EFI文件启动(文件路径)
这次启动失败了,死机?退出主界面?或者重新尝试?结果因机器而异
硬件扫描和准备
启动()
所以,总结说来,UEFI 并不是单纯地找到启动设备上的 EFI 分区里的某固定文件路径就启动了,而是会结合 NVRAM 中的配置来完成整个过程。NVRAM 中的启动配置包括一系列启动项和响应的预设优先顺序,Linux 下可以用 efibootmgr -v 命令查看。它会在以下情况下发生更改:
- 当安装操作系统完成后,操作系统会自动添加一条启动项,如 ubuntu 会生成一条名为
ubuntu的项目并指向 EFI 分区的\EFI\ubuntu\shimx64.efi文件,而 Windows 会生成一条名为Windows Boot Manager的项目指向 EFI 分区的\EFI\Microsoft\Boot\bootmgfw.efi文件。 - 在操作系统中通过
efibootmgr等工具手动修改,使用grub-install命令时如果没有指定--no-nvram也会添加一个启动项 - 在 BIOS(UEFI 不是 BIOS,但是习惯上还是管机器的 UEFI 配置界面叫 BIOS)中修改
- 由于 Windows 用户实在太多了,有的主板生产商会自动为 Windows 添加启动项(避免小白搞坏配置),甚至强制将其列为第一优先级(这就很恶心了)
- 手动从某个设备成功启动后,硬件可能会自动地将其对应的 EFI 文件加入启动项(因硬件而异)
- 硬件检测到不存在的设备/文件,自动删除启动项(因硬件而异)
- ……
总之这个 NVRAM 中的启动项配置的编辑策略各不相同,甚至不排除可能有的主板出于安全(恰烂钱)考虑拒绝你对它进行编辑,强制你从 Windows 启动,所以需要根据自己的电脑自行实验。一般来说,台式机自由度比较大,绝大多数都是允许你自行编辑的,且不会强制更改你自定义优先级;而高端或者新的笔记本也和台式机差不多,但是又老又旧的笔记本的坑就比较多了。
因此,到这里就可以解释如下现象了:
为啥删除了 EFI 分区下的
\EFI\BOOT目录也还能正常启动?因为 NVRAM 中的启动项配置直接指向
\EFI\ubuntu或者\EFI\microsoft目录,不依赖于\EFI\BOOT,\EFI\BOOT下的东西只在直接从该设备启动时生效,相当于作为一个后备。为啥换了主板后就只能从 Windows 启动了?
NVRAM 在主板上,换了后自然全没了,剩下 Windows 是因为前面说的有的主板为了方便小白使用自动探测 Windows 启动项的存在并添加之,Linux 就没福气了,这需要可以通过引导盘进入 Live 环境后用
efibootmgr修复。为什么安装 Linux 后安装 Windows,无法进入 Linux(Grub)?
还是能进入的,只不过 Windows 把自己设置成了第一优先级,默认直接进 Windows;所以一般先装 Windows 再装 Linux,这样 Linux 把自己设置成第一优先级,可以在 Grub 界面二选一。不过其实这都是小问题,改一下 NVRAM 配置就行,并不是很多教程说的必须先装 Windows,没那么死板。
关于安全启动(SecureBoot)
所有教你安装 Ubuntu 之前关闭 SecureBoot 的教程可以一律拉黑
2020 年了,Ubuntu 它早就向微软购买了安全启动认证密钥,所以它的 \EFI\ubuntu\shimx64.efi 文件是可以通过机器安全启动校验的。这个 EFI 文件的作用就是作为一个入口跳板,通过 SecureBoot 后再去加载后续 EFI 文件完成启动操作。
Grub 是如何被加载的
\EFI\ubuntu\shimx64.efi 同目录下有一个 \EFI\ubuntu\grub.cfg。内容如下:
search.fs_uuid 62c89a8b-9df9-4acd-bc18-8622a8d501d0 root hd0,gpt2
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg
第一行的 uuid 是我 Ubuntu 系统所在分区的 uuid,(似乎 root 后的 hd0,gpt2 可以省略,删掉也没影响)。找到这个分区后,根据第二第三行的配置,Grub 的 EFI 部件会从中读取 /boot/grub/grub.cfg 文件作为 grub 启动配置(该文件其实就是执行 grub-update 的输出)。
然后,就是展示你熟悉的 Grub 窗口,再然后根据选择的 Grub 启动项进入操作系统,不过这已经不是 UEFI 的范畴了。
系统安装时候的一个天坑
想象中,最理想的形式应该是系统安装时可以让用户自主选择安装到哪个 EFI 分区,这对双硬盘/移动硬盘用户绝对是好东西,可以保证每个硬盘上的系统互相独立,拔掉硬盘到别的电脑上也能继续用。
但是!
Windows 就不必说了,向来是帮用户做决定,直接自作主张安装到它找到的第一个 EFI 分区中去,不让你选择。
Ubuntu 倒是有一个界面让你选择安装启动引导器的设备,不过这好像是一个陈年老 bug,一直没有修复,它也和 Windows 一样,也是不理会选择结果,直接奔着扫描到的第一个 EFI 分区去了。参见 ubuntuforums。

所以,如果真的想要做到双硬盘独立的双系统,你可以这么事后补救:
- 进入第二个系统
umount现有的/boot/efi并重新挂载上正确的分区(应该是空的)- 使用
grub-install,重建缺失的 EFI 文件 - 修改
/etc/fstab更正挂载到挂载选项,否则即使会在正常启动操作系统后挂载上错误的/boot/efi目录 - 恢复第一个操作系统的 EFI 分区(因为默认的安装策略修改了它)
- 如果第一个系统是同版本 ubuntu,那么需要修正旧的 EFI 分区下的
\EFI\UBUNTU\grub.cfg文件中的 uuid 即可,把它改回去指向第一个系统的根目录 - 如果是其他的 Linux 系统,可以重启回第一个操作系统后使用
grub-install重构它的 EFI 分区 - 如果第一个系统是 Windows,那么应该删除 EFI 分区下的整
ubuntu目录和BOOT目录下的所有子文件,并把\EFI\Microsoft\Boot\bootmgfw.efi复制到\EFI\BOOT\Bootx64.efi(EFI 文件系统不分文件名大小写)
- 如果第一个系统是同版本 ubuntu,那么需要修正旧的 EFI 分区下的
- 使用
efibootmgr调整启动项目(就是修改 NVRAM 配置)
上面的操作都挺麻烦的,其实最简单的办法还是防止这种事情发生,原理很简单,把无关硬盘在本次装机运行周期内移除。可以在 Try Ubuntu 环境下执行如下操作。
对于
SATA类型的硬盘(设备号形如/dev/sda),方法很简单echo 0 | sudo tee /sys/block/<如sda>/device/delete而对于
NVME类型的硬盘(设备号形如/dev/nvme0n1),就有些麻烦了# 查看设备的PCI路径 realpath /sys/block/<如nvme0n1> # 输出结果应该大致为 /sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1 # 然后根据输出结果,将对应的NVME设备移除 echo 1 | sudo tee /sys/devices/pci0000:00/0000:00:0e.0/remove或者直接一行命令
echo 1 | sudo tee $(realpath /sys/block/nvme0n1)/../../../remove当然,如果是台式机,那还可以使用物理学方法,没那么多花里胡哨的,关机拔线就完事了
此命令会在将整个磁盘设备彻底隐藏(重启后恢复),因此当然不需要担心安装到了错误的 EFI 分区了。
验证与自查
ls /sys/firmware/efi存在内容 → 当前是 UEFI 启动(而非 Legacy BIOS)sudo efibootmgr -v可查看 NVRAM 中的启动项、指向的 EFI 文件路径与启动顺序- 删除 EFI 分区的
\EFI\BOOT目录后仍能正常启动(理解 NVRAM 直接指向\EFI\ubuntu等目录,\EFI\BOOT只是后备) - 双系统出现「只能进 Windows」时,用
efibootmgr重建/调整 Linux 启动项