从零写OS内核 | Windows Boot Manager vs GRUB:两种引导加载器的完整解析

从 360 到 362,我们讲了 BIOS/UEFI → MBR/GPT → GRUB 的启动链路。GRUB 是 Linux 的标配 bootloader,但 Windows 用的是另一套完全不同的系统——BOOTMGR(Boot Manager)和 BCD(Boot Configuration Data)。

你有没有想过这些问题:

  • Windows 的”系统保留分区”(System Reserved)里到底装了什么?
  • 为什么重装 Windows 会把 GRUB 覆盖掉,但反过来不会?
  • bcdedit 命令改的到底是什么东西?
  • 为什么 Windows 10/11 可以从 VHD 文件启动,而不需要虚拟磁盘?
  • GRUB 1 和 GRUB 2 有什么区别?为什么有些系统还在用 GRUB 1?

今天,我们来深入拆解这两种引导加载器——Windows 的 BOOTMGR + BCD,以及 GRUB 的 Stage 1/Stage 2 进化史。


Windows 引导加载器:BOOTMGR 的完整链路

三种固件模式,三种启动链路

Windows 的启动链路由固件类型决定:

Bash
Legacy BIOS + MBR:
  POST → MBR(0x7C00) → BOOTMGR(C:BootBOOTMGR) → {BCD 在 C:}

UEFI + GPT(最常见):
  POST → UEFI → NVRAM BootOrder → EFIMicrosoftBootbootmgfw.efi
  → BCD(C:BootBCD) → winload.efi → ntoskrnl.exe

Legacy BIOS + GPT(少见):
  POST → MBR(Protective) → BIOS Boot Partition(GRUB/BOOTMGR 共存)
  → BOOTMGR → BCD

我们重点讲最常见的 UEFI + GPT 链路。

BOOTMGR 是什么

BOOTMGR 是 Windows 的 Boot Manager,一个 32 位的压缩可执行文件(NTFS 压缩属性开启)。它的职责是:

  1. 读取 BCD(启动配置数据)
  2. 显示启动菜单(如果有多条启动项)
  3. 加载 Windows 启动加载器(winload.exe / winload.efi)

Bash
BOOTMGR 文件位置(UEFI 模式):
C:EFIMicrosoftBootbootmgfw.efi

BCD 文件位置:
C:BootBCD        (UEFI 模式下在 ESP 根目录的 Boot 里)

BOOTMGR 依赖的系统文件:
C:WindowsSystem32winload.efi   ← 真正的 Windows 内核加载器
C:WindowsSystem32ntoskrnl.exe  ← Windows 内核本体

System Reserved 分区(系统保留分区)

安装 Windows 时,如果用的是 MBR 磁盘,Windows 会自动创建一个 100MB 的 System Reserved 分区(也称”系统分区”)。这个分区包含:

Bash
System Reserved 分区内容:
├── Boot                          ← 启动文件夹
│   ├── BCD                        ← 启动配置数据
│   ├── BOOTMGR                    ← Windows Boot Manager
│   ├── fonts                     ← 启动字体(中文等)
│   └── Resources                ← 启动动画资源
└── System Volume Information     ← 锁定的元数据目录

作用:即使 Windows 的 C 盘损坏或被格式化,System Reserved 分区里的 BOOTMGR + BCD 还在,可以启动修复环境(WinRE)。这也是为什么 Windows 10/11 安装时默认勾选”为 Windows 创建系统分区”——它不是浪费空间,是保命分区。

GPT 模式下不需要这个分区:UEFI 模式下,ESP(EFI System Partition)承担了 System Reserved 的角色,BCD 和 BOOTMGR 都存在 ESP 的 Boot 目录里。

BCD:启动配置的”注册表”

BCD(Boot Configuration Data)不是普通文件,是 Windows 的一个注册表配置单元(Registry Hive)。它存储在 BootBCD,和 Linux 的 grub.cfg 承担类似的信息角色——”从哪里加载内核”、”启动参数是什么”、”超时多久”——但格式完全不同。

BCD 用 registry 格式存储,里面有多个键,常见的有:

Bash
BCD 内容(bcdedit /store C:BootBCD /enum all 的简化视图):

{bootmgr}
  device          = partition=C:          ← BOOTMGR 在哪个分区
  locale          = zh-CN                 ← 启动菜单语言

{default}
  description     = Windows 10             ← 默认启动项名称
  osdevice        = partition=C:          ← Windows 在哪个分区
  device          = partition=C:
  path            = Windowssystem32winload.efi  ← 内核加载器路径
  systemroot      = Windows

{memdiag}
  description     = Windows Memory Diagnostic  ← 内存诊断工具

{ntfs}
  description     = 从 VHD 启动 (TEST)    ← VHD 启动项
  filepath        = VHDstest.vhdx
  devices         = vhd

bcdedit:Windows 的”GRUB 配置编辑器”

Linux 用文本编辑器改 grub.cfg,Windows 用 bcdedit.exe 命令行操作 BCD:

Cmd
# 查看所有启动项(需要管理员权限)
bcdedit /enum all

# 查看当前启动项详细信息
bcdedit

# 修改启动项名称
bcdedit /set {default} description "Windows 10 Pro"

# 设置默认操作系统(从 GRUB 跳 Windows 时用)
bcdedit /default {default}

# 添加启动参数(禁用 DEP 等)
bcdedit /set "{default}" nx AlwaysOff

# 创建 VHD 启动项
bcdedit /copy {default} /d "Windows from VHD"
bcdedit /set "{new}" device vhd=[C:]VHDswin10.vhdx
bcdedit /set "{new}" osdevice vhd=[C:]VHDswin10.vhdx
bcdedit /set "{new}" path Windowssystem32winload.efi

启动过程:winload.exe 之前发生了什么

Bash
UEFI 固件 → NVRAM BootOrder →
  → EFIMicrosoftBootbootmgfw.efi(UEFI 模式)
  → 挂载 FAT32 ESP,读 BootBCD
  → 显示启动菜单(或直接加载默认项)
  → 读取 BCD 的 {default} / osdevice
  → 定位 Windows 分区(C:)
  → 加载 WindowsSystem32winload.efi
  → 验证启动签名(Secure Boot)
  → 读取 WindowsSystem32configSYSTEM 注册表(获取驱动列表)
  → 加载内核 ntoskrnl.exe + 驱动
  → 启动 Windows

Bash
Legacy BIOS 模式:
POST → MBR → BOOTMGR(从 System Reserved 分区加载)
→ 读 C:BootBCD → winload.exe(非 EFI 版本)→ ntoskrnl.exe

WinRE(Windows Recovery Environment)

Windows 安装后,会在系统分区里预留 WinRE 的路径:

Cmd
# 检查 WinRE 状态
 reagentc /info

# 输出示例:
# Windows Recovery Environment (Windows RE)
# 位置: ?GLOBALROOTdeviceharddisk0partition1RecoveryWindowsRE
# WinRE 启用: true

如果 Windows 启动失败,按 F8 或(在 Win 10/11 里)长按电源键强制关机 3 次,Windows 会自动进入 WinRE——它实际上是一个精简的 Windows PE 环境,可以运行 diskpartbcdeditsfc /scannow 等修复工具。

VHD 原生启动:Windows 的”虚拟机启动”

从 Windows 7 之后,Windows 支持从 VHD(Virtual Hard Disk) 文件原生启动——不需要虚拟机,直接把 VHD 当作物理磁盘:

Bash
工作原理:
1. BCD 里有专门的 {ntfs} 条目,device=vhd
2. BOOTMGR 识别 VHD 类型,把 VHD 文件映射为"虚拟磁盘"
3. 从 VHD 里的 Windows 启动(和从物理盘启动完全一样)

# 创建 VHD 并附加为启动盘
diskpart
create vdisk file=C:VHDswin10.vhdx maximum=60000 type=fixed
attach vdisk

这和 Linux 的 LVM 逻辑卷类似——逻辑卷不需要分区表,直接被当作”物理分区”用。


GRUB:完整阶段解析

GRUB 1(GRUB Legacy):Stage 结构

最初的 GRUB(现在叫 GRUB Legacy)设计于 1999 年,它的工作方式非常直白:

Bash
GRUB Legacy 启动阶段(MBR + 文件系统配合):

Stage 1(MBR,446 字节):
  └── 放在 MBR 里,跳到 Stage 2(或 Stage 1.5)

Stage 1.5(MBR 后扇区,可选):
  └── 理解文件系统(ext2/reiserfs/fat),找到 Stage 2
  └── e2fs_stage1_5 → ext2 文件系统支持
  └── ffs_stage1_5  → UFS/FFS 支持
  └── ffs_stage1_5  → FAT 文件系统支持

Stage 2(/boot/grub/stage2):
  └── GRUB 主程序,读取 menu.lst
  └── 显示启动菜单
  └── 加载内核 + initrd

为什么要有 Stage 1.5

Stage 1 只有 446 字节,放不下文件系统驱动——它只知道”从哪个扇区读下一段代码”。Stage 1.5 把文件系统驱动塞进 MBR 后的扇区(通常占用几十个扇区),让 GRUB 可以读取 /boot/grub/stage2

没有 Stage 1.5,GRUB 无法理解文件系统,就找不到 Stage 2——Stage 2 在 /boot/grub/ 目录里,不在固定扇区。

GRUB 2(GRUB):模块化重写

2005 年后,GRUB 进行了完全重写,变成了 GRUB 2。主要变化:

特性 GRUB Legacy GRUB 2
配置文件 menu.lst grub.cfg(自动生成,勿手动编辑)
阶段结构 Stage 1 / 1.5 / 2 boot.img + core.img(动态生成)
模块化 无(文件系统驱动硬编码) 模块按需加载(.mod 文件)
分区支持 MBR 为主 MBR + GPT + UEFI
安装方式 grub-install(简单) grub-install(自动生成 core.img)
脚本支持 条件判断、循环、变量

GRUB 2 的引导阶段

Bash
UEFI GRUB 2(最常见):

EFIubuntugrubx64.efi(或 shimx64.efi for Secure Boot)
  ├── 加载 grub.cfg
  ├── 加载模块(.mod 文件,从 /boot/grub/x86_64-efi/ 目录)
  ├── 读取 menu entries
  └── 加载内核 + initrd

MBR GRUB 2:

boot.img(446 字节,MBR)→ core.img(嵌入式,包含基础模块 + 启动加载器)
  └── boot.img 跳到 core.img(core.img 大小约 32KB,包含:
      - 基础文件系统和驱动(ext2/fat/nfs 等)
      - 压缩模块加载器
      - 启动菜单和内核加载逻辑)
  └── 从 /boot/grub/ 加载更多 .mod 模块
  └── 读取 grub.cfg

GRUB 2 的配置文件:grub.cfg

GRUB 2 的配置文件不是手写的——它是 grub-mkconfig(即 update-grub自动生成的:

Bash
# 生成 /boot/grub/grub.cfg(自动检测内核和其他 OS)
sudo grub-mkconfig -o /boot/grub/grub.cfg

# 或者(Debian/Ubuntu)
sudo update-grub

grub-mkconfig 会扫描:

  • /boot/vmlinuz-*(Linux 内核)
  • /boot/initrd-*(initramfs)
  • /etc/grub.d/ 下的自定义脚本(40_custom 等)
  • /etc/default/grub(全局默认参数)
  • 其他分区上的 OS(Windows bootmgfw.efi、Mac 等)

生成的 grub.cfg 示例:

Bash
# /boot/grub/grub.cfg(自动生成,不要手动改)

set timeout=5
set default=0

menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu {
    recordfail
    load_video
    gfxmode $linux_gfx_mode
    insmod gzio
    insmod part_gpt
    insmod ext2
    search --no-floppy --fs-uuid --set=root 5a3c9f2b-1d2e-4f8a-9c3b
    linux   /boot/vmlinuz-6.5.0-rc7 root=UUID=5a3c9f2b-1d2e-4f8a-9c3b ro quiet splash
    initrd  /boot/initrd.img-6.5.0-rc7
}

menuentry 'Windows 10 (on /dev/sda1)' {
    insmod ntfs
    search --no-floppy --fs-uuid --set=root A2B3C4D5
    chainloader +1
}

/etc/default/grub:GRUB 2 的全局配置

grub.cfg 是自动生成的,手动改它没有意义(下次 update-grub 就没了)。真正的全局配置在 /etc/default/grub

Bash
# /etc/default/grub
GRUB_DEFAULT=0           # 默认启动项(0=第一个,saved=记住上次选择)
GRUB_TIMEOUT=5           # 超时(秒)
GRUB_TIMEOUT_STYLE=hidden  # 隐藏菜单(按 ESC 显示)
GRUB_CMDLINE_LINUX="quiet splash"  # 内核启动参数
GRUB_DISABLE_RECOVERY="true"  # 不显示 recovery 模式

# 生成 grub.cfg 后,如果想永久改 timeout,改这里的值,再 update-grub

GRUB 2 的模块系统

GRUB 2 把功能拆成 .mod 模块,按需加载。常见模块:

模块 功能
linux.mod 加载 Linux 内核
initrd.mod 加载 initramfs
chainloader.mod 链式加载另一个 bootloader(如 Windows)
search.mod 按 UUID/LABEL/PARTUUID 查找设备
part_gpt.mod GPT 分区支持
ext2.mod ext2/ext3/ext4 文件系统支持
ntfs.mod NTFS/FAT 文件系统支持
linux16.mod 16 位实模式启动(旧内核兼容)

GRUB 2 安装到 UEFI 系统

Bash
# 安装 GRUB 到 ESP(UEFI 模式)
sudo grub-install /dev/sda
# 在哪里?/boot/grub/(文件)+ /EFI/ubuntu/(.efi 文件)

# 重新生成配置
sudo update-grub

# 查看 ESP 内容
ls /boot/efi/EFI/
# 输出:
# EFI/
#   ubuntu/         ← GRUB 的 .efi
#   Microsoft/      ← Windows Boot Manager
#   Boot/           ← 默认回退 .efi(BOOTX64.EFI)

Windows + GRUB 双系统:从安装到修复

安装顺序决定谁控制 MBR

先装 Windows,后装 Linux(推荐)

Bash
① Windows 装完 → MBR 有 BOOTMGR,Bcdedit 可用
② 安装 Linux → GRUB 安装到 MBR → 覆盖 BOOTMGR
③ GRUB 检测 Windows → 自动在 grub.cfg 里生成 chainloader 条目
④ 启动顺序:GRUB 菜单 → Windows → Ubuntu

先装 Linux,后装 Windows(麻烦)

Bash
① Linux 装完 → GRUB 在 MBR
② Windows 安装 → 覆盖 MBR → BOOTMGR 替换 GRUB → Linux 无法启动
③ 修复:Linux Live USB → chroot → grub-install

Windows Boot Manager 的 NVRAM 条目

在 UEFI 模式下,Windows 和 GRUB 都在 NVRAM 里注册了自己的启动项:

Bash
# 查看所有启动项(Linux)
sudo efibootmgr -v

# 示例输出:
# BootCurrent: 0001
# BootOrder: 0001, 0000
# Boot0000* Windows Boot Manager   HD(1,GPT,...)/File(EFIMicrosoftBootbootmgfw.efi)
# Boot0001* ubuntu                HD(2,GPT,...)/File(EFIubuntushimx64.efi)

Cmd
# 查看所有启动项(Windows,管理员权限)
bcdedit /enum firmware

# 输出显示 NVRAM 里的启动项(UEFI 模式下)

从 GRUB 链式加载 Windows

GRUB 不能直接加载 Windows 内核(Windows 用的是 PE/COFF .efi 格式,但 BOOTMGR 不是标准 ELF)。解决方法是链式加载

Bash
# grub.cfg 里的 Windows 启动条目
menuentry 'Windows 10' {
    insmod ntfs                           # 加载 NTFS 读驱动
    search --no-floppy --fs-uuid --set=root A2B3C4D5E6F7  # 找 Windows 分区
    chainloader ($root)/EFI/Microsoft/Boot/bootmgfw.efi   # 加载 BOOTMGR
    boot                                  # 跳转
}

chainloader +1 的意思是”把这个分区的第一个扇区当作 bootloader 加载”,也就是加载这个分区根目录的 EFIMicrosoftBootbootmgfw.efi

修复 GRUB:标准操作流程

最常见的问题:重装 Windows 后 GRUB 消失。

Bash
# 1. 用 Linux Live USB 启动(Arch/Ubuntu 均可)
# 2. 找到 Linux 根分区
sudo fdisk -l /dev/sda
# 假设 Linux root 是 /dev/sda2

# 3. 挂载
sudo mount /dev/sda2 /mnt
sudo mount /dev/sda1 /mnt/boot   # 如果 /boot 是独立分区
sudo mount --bind /dev /mnt/dev
sudo mount --bind /sys /mnt/sys
sudo mount --bind /proc /mnt/proc

# 4. chroot 进入系统
sudo chroot /mnt

# 5. 重新安装 GRUB(UEFI 模式)
grub-install /dev/sda
update-grub   # 自动检测 Windows

# 6. 退出重启
exit
sudo reboot

修复 BOOTMGR(Windows 环境)

Cmd
# 用 Windows 安装盘(或 WinRE)启动
# 选择"修复计算机" → "疑难解答" → "命令提示符"

# 重建 BCD
bootrec /fixmbr        # 重建 MBR(不破坏分区)
bootrec /fixboot       # 修复启动扇区(写新 BOOTMGR)
bootrec /scanos        # 扫描可启动的 Windows 安装
bootrec /rebuildbcd    # 重建 BCD

# 如果 bootrec 失败,手动用 bcdboot 重建
bcdboot C:Windows /s C: /f UEFI
#   /s C:  →  指定 ESP 盘符
#   /f UEFI → 指定为 UEFI 模式

Secure Boot:签名验证的游戏规则

签名链:shim → GRUB → 内核

在 Secure Boot 模式下,不是所有 .efi 都能启动——必须签名:

Bash
Secure Boot 启动链(启用签名时):

UEFI 固件(内置微软公钥)
  ↓
shimx64.efi(微软签名,Ubuntu/Red Hat 等发行版自带)
  ↓(验证)
grubx64.efi(发行版签名,shim 验证)
  ↓(验证)
vmlinuz(内核镜像,发行版签名)
  ↓
initramfs(initrd,通常内嵌在 kernel 里或单独签名)

shim 的作用:shim 是微软签名的小型垫片(shim=垫片),它内置了发行版的公钥——这样,即使内核不是微软签名的,只要发行版在 shim 里注册了自己的公钥,就能启动。Ubuntu、Red Hat 都提供带 shim 的 GRUB。

禁用 Secure Boot(开发用)

如果需要加载未签名的 .efi(比如自己编译的 GRUB 或内核):

Bash
# 在 Linux 里查看 Secure Boot 状态
mokutil --sb-state
# SecureBoot enabled  → 已启用
# SecureBoot disabled → 已禁用

# 在 UEFI 设置里禁用(按 Delete 或 F2 进入 BIOS 设置)
# Boot → Secure Boot → Disabled

踩坑与注意事项

坑 1:Windows 安装时没有创建系统保留分区(MBR 模式)

现象:Windows 安装在已有 Linux 的磁盘上,安装程序问”把 Windows 安装在哪里”,选了整块盘,但”系统保留分区”灰色无法勾选。

原因:MBR 模式,安装程序检测到已有多个分区,不敢动。

解决:用 Disk Management 先压缩出一个 100MB 的未分配空间,再装 Windows,让安装程序自动用它创建 System Reserved。

坑 2:grub.cfg 被覆盖(手动改了 grub.cfg,update-grub 后丢失)

原因:grub.cfgupdate-grub 自动生成的,手动改无效。

解决:在 /etc/grub.d/40_custom 里添加自定义条目,这是保留区,update-grub 不会覆盖它。

Bash
# /etc/grub.d/40_custom(这是你唯一应该手动编辑的文件)
menuentry 'My Custom Entry' {
    set root='hd0,gpt2'
    linux /boot/vmlinuz-linux root=/dev/sda2 quiet
    initrd /boot/initrd.img-linux
}

坑 3:GRUB 命令行能用,但菜单无法启动

现象:GRUB 菜单出现,但选 Ubuntu 就黑屏;按 c 进入命令行手动 linux /boot/vmlinuz... 可以启动。

原因:自动生成的内核路径或 UUID 错误,通常是 /etc/default/grubGRUB_CMDLINE_LINUX_DEFAULT 有问题,或者磁盘 UUID 变了。

解决:在 GRUB 命令行里 set root=(hd0,gpt2) + linux ... + initrd ... + boot 手动启动,然后 sudo update-grub 重建配置。

坑 4:Windows BCD 损坏导致”启动设备未找到”

现象:UEFI 模式下,BIOS 可以看到磁盘,但按任意键后屏幕空白。

原因:BCD 文件损坏(可能因为磁盘清理工具误删)。

解决:Windows 安装盘启动 → 修复 → 命令提示符 → bootrec /rebuildbcd


写在最后

Windows Boot Manager 和 GRUB,是两条完全不同的设计哲学:

BOOTMGR + BCD GRUB
配置格式 BCD(二进制注册表 hive) grub.cfg(文本文件)
配置工具 bcdedit(Windows 专有) grub-mkconfig / update-grub
多系统 BOOTMGR 只能链式加载 Windows GRUB 可以直接启动 Linux + 链式加载 Windows/macOS
模块化 不可扩展(固件固定) 模块化(.mod 按需加载)
固件集成 NVRAM 启动项(UEFI) NVRAM + MBR 两套
Secure Boot 签名验证(bootmgfw.efi) shimx64.efi 垫片 + GRUB + 内核

核心共同点:都不是操作系统的一部分,是操作系统的搬运工——它们做的工作,都是”把控制权交给 OS 内核”这一件事,区别只在”怎么找内核”、”找不到了怎么办”。

wandos 目前没有自己的 bootloader 实现,依赖 QEMU 的 -kernel 参数直接加载 ELF——如果要支持真实磁盘启动,需要先实现 GRUB 兼容的 Multiboot header(360 篇已讲过),然后参照 GRUB Stage 1/2 的设计,实现一个简化的 stage 加载器。

下篇预告(364):候选方向——文件系统(一):ext2 的 inode、块组与目录项,或者内存管理:Buddy System + SLAB 分配器的协作。你选哪个?

仓库https://github.com/golang12306/os-kernel-from-scratch


相关阅读

  • Windows BCD 存储格式:微软文档 BCD WMI Provider
  • GRUB 2 源码:https://www.gnu.org/software/grub/
  • UEFI Spec:Secure Boot 部分,https://uefi.org/specifications
  • Linux Kernel Source: arch/x86/boot/(Linux 的 boot protocol 实现)
  • https://github.com/zhangfuwen/wandos — Linux 内核教程

动手环节

想深入理解本文内容?动手实践是最好的方式:

今天的目标:在你的机器上同时操作 Windows BCD 和 GRUB 配置,体验两种系统的差异。

  1. 在 Linux 上查看 GRUB 配置生成过程

    Bash
    # 查看 grub.cfg 是怎么生成的
    sudo grub-mkconfig --print  # 不写入文件,只看生成结果
    # 注意:不要修改 /boot/grub/grub.cfg,直接改会被覆盖
    # 正确做法是:改 /etc/default/grub 或 /etc/grub.d/40_custom
  2. 查看 NVRAM 里的启动项

    Bash
    sudo efibootmgr -v
    # 看看有没有 Windows Boot Manager 条目
    # 如果没有,手动添加:
    sudo efibootmgr -c -d /dev/sda -p 1 -l "EFIMicrosoftBootbootmgfw.efi" -L "Windows"
  3. 在虚拟机里练习修复(无风险):

    Bash
    # 用 QEMU 创建双系统虚拟机
    # 先装 Windows,再装 Linux,观察 GRUB 菜单如何出现
    # 然后重装 Windows,观察 GRUB 消失后的修复过程
  4. 分析 BOOTMGR 的结构(如果有 System Reserved 分区):

    Bash
    # 挂载 Windows System Reserved 分区(需要 root)
    sudo mkdir /mnt/win-sysres
    sudo mount /dev/sda1 /mnt/win-sysres -o ro
    ls -la /mnt/win-sysres/Boot/
    # 看看 BCD 和 BOOTMGR 的大小和修改时间

提示:BCD 是一个注册表 hive,需要 bcdedit(Windows)或 bcdtools(Python 库)解析。在 Linux 上直接看 BCD 很难读懂内容,主要靠 efibootmgr 查看 NVRAM 条目。


仓库:https://github.com/golang12306/os-kernel-from-scratch

最后修改: 2024年2月18日

作者

评论

发表评论

您的邮箱地址不会被公开。