从零写OS内核 | GPT 分区:MBR 的 2TB 上限怎么破,128 个分区怎么管
上一篇文章我们讲了 MBR——512 字节的第一个扇区,446 字节 bootloader 加 64 字节分区表。MBR 统治了 PC 分区标准 30 年,但它有两个绕不过去的硬伤:最多 4 个分区,和最大 2TB 磁盘容量。
进入 2000 年代,大容量硬盘普及,MBR 开始成为瓶颈。Intel 牵头设计了新一代分区标准——GPT(GUID Partition Table),它不是 MBR 的升级版,而是一次完全重写:分区数从 4 个扩展到 128 个,容量上限从 2TB 跳到 8ZB(ZettaByte),启动方式也从”磁盘第一个扇区”变成了”专用 EFI 分区”。
今天,我们来拆解 GPT 的完整结构——它是怎么组织的,MBR 原来承担的功能去哪了,以及 GPT 带来了哪些 MBR 根本没有的新玩法。
MBR 的三件事,GPT 全重新做了
MBR 在磁盘上”一肩挑”了三件完全不同的事:
| MBR 承担的功能 | 具体内容 | GPT 的对应方案 |
|---|---|---|
| 启动代码 | BOOTMGR / GRUB Stage 1,占 446 字节 | 移到 ESP(EFI System Partition) 里,不再在 MBR 里 |
| 分区表 | 4 条 × 16 字节 = 64 字节,最多 4 个主分区 | 独立分区表区域(128 条 × 128 字节 = 16KB),在 LBA 2-33 |
| 启动标志 | 0x55AA,判断磁盘是否可启动 | 不需要,GPT Header 里用 CRC32 校验替代 |
MBR 把三件完全不同性质的事混在一起——代码 + 数据 + 元数据。GPT 的思路是:启动的事归启动,分区的事归分区,各司其职,互不干扰。
GPT 的物理布局:磁盘是怎么排列的
GPT 把磁盘从头到尾分成了几个区域,每个区域有明确的职责:
GPT 磁盘完整布局(以 1TB 磁盘为例):
┌─────────────────────────────────────────────────────────────────┐
│ LBA 0(512 字节):Protective MBR │
│ • 防止旧系统误识别 GPT 磁盘为"未分区" │
│ • 第一条分区条目:type=0xEE,start=1,size=整个磁盘 │
│ • 最后两字节:0x55AA(让 BIOS 认为这是"可启动"的 MBR 磁盘) │
├─────────────────────────────────────────────────────────────────┤
│ LBA 1(512 字节):GPT Header │
│ • 签名 "EFI PART"(46 49 52 50 41 52 54) │
│ • 分区表位置(在 LBA 2) │
│ • 分区表大小(128 条 × 128 字节 = 16KB) │
│ • 分区条目数量(128) │
│ • 当前磁盘的 GUID(唯一标识符) │
│ • 备份 GPT Header 位置(LBA -1,即倒数第二扇区) │
├─────────────────────────────────────────────────────────────────┤
│ LBA 2 - LBA 33(32 扇区 = 16KB):主分区表 │
│ • 128 条分区条目,每条 128 字节 │
│ • 总容量:128 × 128 = 16,384 字节(正好 32 个扇区) │
├─────────────────────────────────────────────────────────────────┤
│ LBA 34+:用户数据区(分区实际开始的地方) │
│ 分区 1:ESP(EFI System Partition,FAT32,≥100MB) │
│ 分区 2:Linux root │
│ 分区 3:Linux swap │
│ 分区 N:... │
├─────────────────────────────────────────────────────────────────┤
│ 倒数第 2 扇区(LBA -1):备份 GPT Header │
│ • GPT Header 的完整副本,CRC32 校验 │
│ • 磁盘损坏时能恢复 GPT 信息 │
├─────────────────────────────────────────────────────────────────┤
│ 倒数第 1 扇区(LBA -2 开始,16KB):备份分区表 │
│ • 分区表的完整副本,与主分区表内容相同 │
│ • 主分区表损坏时,工具可以读取备份并恢复 │
└─────────────────────────────────────────────────────────────────┘保护性 MBR(LBA 0)的作用:这不是给 GPT 用的,是给不识别 GPT 的旧工具(MS-DOS fdisk、Windows 安装程序)看的——它们看到一个”类型为 0xEE 的分区,占据整个磁盘”,就知道”这个盘我管不了,别动它”,避免误格式化。
分区条目结构:128 字节怎么描述一个分区
GPT 的每条分区记录是 128 字节,容量是 MBR(16 字节)的 8 倍,信息量也丰富得多:
GPT 分区条目(128 字节):
┌──────────┬──────┬────────────────────────────────────────────────┐
│ 偏移 │ 大小 │ 含义 │
├──────────┼──────┼────────────────────────────────────────────────┤
│ 0x00 │ 16B │ 分区类型 GUID(告诉操作系统"这是什么文件系统") │
│ 0x10 │ 16B │ 唯一分区 GUID(这个分区的全球唯一标识) │
│ 0x20 │ 8B │ 起始 LBA(64 位,终于没有 2TB 上限了) │
│ 0x28 │ 8B │ 结束 LBA(64 位) │
│ 0x30 │ 8B │ 属性标志(64 位,位图) │
│ 0x38 │ 72B │ 分区名称(36 个 UTF-16 字符,可显示中文) │
└──────────┴──────┴────────────────────────────────────────────────┘分区类型 GUID(取代 MBR 的 1 字节类型码)
MBR 用 1 字节的类型码(0x07=NTFS,0x83=Linux),但 1 字节只有 256 种可能,不够用且容易冲突——比如 0x83 本意是”Linux Native 分区”,但谁都可以声称自己是 0x83,没有约束力。
GPT 用 16 字节的 GUID(Globally Unique Identifier)代替类型码,每个分区类型有官方分配的 GUID:
| 分区类型 | GUID | 说明 |
|---|---|---|
| ESP(EFI System Partition) | C12A7328-F81F-11D2-BA4B-00A0C93EC93B |
EFI 启动管理器代码 |
| Linux filesystem data | 0FC63DAF-8483-4772-8E79-3D69D8477DE4 |
通用 Linux 数据区(ext4/XFS) |
| Linux swap | 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F |
Linux swap |
| Linux LVM | E6D6D379-F507-44C2-A23C-238F2A3DF928 |
LVM 物理卷 |
| Windows Recovery Environment | DE94BBA4-06D1-4D40-A16A-BFD50179D6AC |
Windows RE |
| Microsoft Basic Data | EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 |
Windows 数据分区(NTFS/FAT) |
16 字节 GUID 的排列组合足够给每种操作系统、每种文件系统、每种特殊用途一个永不冲突的标识符。这是 MBR 类型码完全做不到的事。
分区名称(72 字节 UTF-16)
MBR 的分区没有名称,只有”主分区 1″、”主分区 2″这样的序号。GPT 给每个分区一个 36 字符的 UTF-16 名称——比如”Arch Linux”、”Windows RE”、”Data Volume”,可以显示中文:
0x38 偏移开始的 72 字节(36 × UTF-16):
48 00 61 00 72 00 63 00 20 00 4C 00 69 00 6E 00 # "Arch Linux"
75 00 78 00 20 00 00 00 00 00 00 00 00 00 00 00 # (不足部分填 0)属性标志(64 位位图)
属性标志(64 位)的常用位:
Bit 0:系统分区(System partition)
- OS 启动必需的分区,不能随便删
- 对应 ESP 或 Windows 的 "System Reserved"
Bit 1:EFI 固件隐藏属性
- 固件不显示这个分区
Bit 2:Legacy BIOS 启动分区(BIOS Boot Partition)
- GPT 里用于 BIOS 启动支持(很少用)
Bit 48-63:用户定义属性(每 OS 自己解释)
- Linux 用 Bit 60 表示"必须备份"
- Linux 用 Bit 54 表示"只读"GPT 的新能力:从 4 分区到 128 分区
为什么是 128 个分区
主分区表在 LBA 2-33,占 16KB。每条分区记录 128 字节,所以最多 128 条。
128 × 128 字节 = 16,384 字节 = 32 个扇区(正好填满 LBA 2-33)
如果想支持更多分区?
- 需要更大的分区表 → 更大分区表区域 → 浪费更多空间
- 实际上 128 个分区已经远超任何单磁盘需求
- 如果需要更多,用 LVM 把多块磁盘合并成逻辑卷8ZB 的容量上限
GPT 用 64 位记录起始/结束 LBA。每个 LBA 是 512 字节(标准扇区大小)。
2^64 × 512 = 9.4 ZB(ZettaByte,1 ZB = 10^21 字节)
实际限制:磁盘厂商用 512 字节模拟 4K 物理扇区(4Kn),
或用 4K 原生扇区(4Kn),但 64 位 LBA 对现代磁盘绰绰有余。
主流硬盘(≤20TB)用 LBA 48 位就够了(48 位也支持 128PB)。CRC32 校验
GPT Header 有一个 Header CRC32 字段(偏移 0x10,4 字节)。每次读取 Header 时,内核工具(gdisk / parted)会验证 CRC32 是否匹配,如果不匹配说明 Header 损坏,从磁盘末尾读取备份恢复。
# 用 gdisk 查看 GPT Header CRC
sudo gdisk -l /dev/sda | head -20
# 输出:
# Partition table scan:
# MBR: protective
# BSD: not present
# APM: not present
# GPT: present
#
# Disk /dev/sda: 500118192 sectors, 238.5 GiB
# Model: Samsung SSD 970 EVO Plus 250GB
# Sector size (logical/physical): 512/512
# Partition table holds 128 entries
# Total free space is 0 sectors备份与恢复:GPT 自愈机制
GPT 最大的工程优势是双副本:主分区表在 LBA 2-33,备份在磁盘倒数 16KB;主 Header 在 LBA 1,备份在倒数第二个扇区。任何一边损坏,另一边可以完整恢复。
# 用 gdisk 检查 GPT 的完整性
sudo gdisk /dev/sda
# 在 gdisk 里:
# x → 进入专家模式
# e → 重建主 GPT Header(从备份恢复)
# v → 验证分区表(检查 CRC)
# d → 重建主分区表(从备份恢复)实际恢复案例
假设磁盘的分区表损坏(但数据区完好):
# 1. 用 testdisk 或 gpted 扫描丢失的分区
sudo testdisk /dev/sda
# 2. 或者用 gdisk 从备份恢复
# 启动 gdisk,选择 "r"(恢复)模式
# 选项 "e" 从备份重建主分区表
# 选项 "d" 用备份恢复损坏的主分区表
# 3. 验证修复后的分区表
sudo gdisk -v /dev/sda
# 如果输出 "OK: header CRC matches",修复成功UEFI + GPT:启动链路的完整变化
MBR 时代,bootloader 代码塞在 MBR 的 446 字节里,够塞 GRUB Stage 1 但不够塞 GRUB Stage 2,所以要 Stage 1.5 做文件系统桥接。
UEFI + GPT 完全重新设计了启动链路:
UEFI + GPT 启动链路:
① 上电 → UEFI 固件初始化
② 读取 NVRAM 里的 BootOrder(保存在闪存里)
③ 按 BootOrder 顺序尝试加载 EFIBootBootX64.efi(或 NVRAM 里的自定义条目)
④ 验证 Secure Boot 签名(或跳过验证)
⑤ 找到 .efi 文件后,直接执行 → 控制权交给 Bootloader(如 GRUB)
⑥ GRUB 读取 /boot/grub/grub.cfg,显示启动菜单
⑦ 用户选 Linux → GRUB 加载 vmlinuz + initramfs,跳转执行
用户选 Windows → GRUB chainload EFIMicrosoftBootbootmgfw.efiESP(EFI System Partition)是整个启动链路的核心——它是一个 FAT32 格式的小分区(≥100MB),存放所有 .efi 文件,包括:
EFI/BOOT/BOOTX64.EFI(默认启动项,回退用)EFI/ubuntu/grubx64.efi(Ubuntu 的 GRUB)EFI/Microsoft/Boot/bootmgfw.efi(Windows Boot Manager)EFI/arch/grubx64.efi(Arch 的 GRUB)
ESP 分区内容示例:
/
├── EFI/
│ ├── Boot/
│ │ └── BOOTX64.EFI ← 回退启动项(每台机器都有)
│ ├── ubuntu/
│ │ ├── grubx64.efi ← Ubuntu GRUB
│ │ ├── grub.cfg ← GRUB 配置文件
│ │ └── shimx64.efi ← Secure Boot 签名垫片
│ ├── Microsoft/
│ │ └── Boot/
│ │ └── bootmgfw.efi ← Windows Boot Manager
│ └── arch/
│ └── grubx64.efi ← Arch GRUB
└── .disk_info(可选,元数据)GPT 工具:Linux 上的 GPT 操作
gdisk / sgdisk:最完整的 GPT 工具
# 查看 GPT 分区表
sudo gdisk -l /dev/sda
# 创建新分区
sudo gdisk /dev/sda
# n → 新建分区
# 分区号 → 默认(下一个可用)
# 起始扇区 → 默认
# 结束扇区 → +50G(创建 50GB 分区)
# 类型 → 输入 L 或 hexcode
# 0700 = Microsoft basic data (NTFS/FAT32)
# 8200 = Linux swap
# 8300 = Linux filesystem (ext4/XFS)
# fd00 = Linux LVM
# w → 写入保存
# 高级操作(sgdisk,非交互)
sudo sgdisk --new=1:::+50G /dev/sda # 创建 50GB 分区
sudo sgdisk --typecode=1:0FC63DAF-8483-4772-8E79-3D69D8477DE4 /dev/sda # 设类型为 Linux
sudo sgdisk --change-name=1:"Arch Linux" /dev/sda # 设分区名
sudo sgdisk --delete=1 /dev/sda # 删除分区 1parted:MBR 和 GPT 通吃
# parted 适合创建/删除/调整分区
sudo parted /dev/sda
# 创建 GPT 分区表
(parted) mklabel gpt
# 创建分区
(parted) mkpart primary ext4 0% 100% # 占满整个盘
(parted) mkpart primary fat32 0MB 512MB # 创建 512MB ESP
# 查看
(parted) print
# 删除
(parted) rm 2
# 退出
(parted) quitfdisk 也支持 GPT(但功能有限)
# fdisk -l 也能显示 GPT 信息(只读)
sudo fdisk -l /dev/sda
# 实际上 fdisk 更推荐用于 MBR,GPT 用 gdisk
# 因为 fdisk 对 GPT 的操作容易出错(GPT 结构比 MBR 复杂得多)gptedit:GUI 工具
# 如果习惯 GUI
sudo apt install gpted
gpted /dev/sda
# 点几下就能创建/删除/调整 GPT 分区用 dd 备份 GPT(注意别破坏分区)
# 备份主 GPT Header(LBA 1)
sudo dd if=/dev/sda of=/home/$USER/gpt-header-backup.img bs=512 count=1 skip=1
# 备份分区表(LBA 2-33,共 32 个扇区)
sudo dd if=/dev/sda of=/home/$USER/gpt-parttable-backup.img bs=512 count=32 skip=2
# 备份 Protective MBR(LBA 0)
sudo dd if=/dev/sda of=/home/$USER/protective-mbr.img bs=512 count=1
# 恢复(从备份文件)
sudo dd if=/home/$USER/gpt-header-backup.img of=/dev/sda bs=512 count=1 seek=1
sudo dd if=/home/$USER/gpt-parttable-backup.img of=/dev/sda bs=512 count=32 seek=2MBR → GPT 转换:不丢数据的迁移
想把 MBR 磁盘换成 GPT,但不想重装系统?Linux 的 gdisk 可以帮你做这件事:
# 警告:操作前务必备份数据!
# 1. 用 fdisk 确认当前是 MBR
sudo fdisk -l /dev/sda
# 输出里如果有 "GPT is not present" 说明是 MBR
# 2. 用 gdisk 打开这个磁盘
sudo gdisk /dev/sda
# 3. gdisk 会检测到 MBR 分区表,询问:
# "Found valid MBR with 3 partitions"
# "Convert to GPT? (Y/N):"
# 输入 Y
# 4. gdisk 会保留所有原有分区(只转换元数据,不改数据区)
# 分区内容、文件系统、数据全部保留
# 5. 查看转换结果
# p → 显示分区表
# w → 写入保存
# 6. 验证
sudo gdisk -l /dev/sda | grep "GPT:"
# 输出:GPT is present关键点:MBR → GPT 转换不会破坏磁盘上的数据——它只改变了分区表的格式。分区表从 MBR 的 64 字节格式,转换为 GPT 的 16KB 格式,原有数据的起止位置不变。
但要注意:
- Windows 用户:Windows 需要用
mbr2gpt.exe工具,否则直接转换会破坏 Windows 的启动路径(UEFI 的 BCD 存储依赖 ESP 分区) - BIOS 模式系统:如果原本是 Legacy BIOS 启动,换成 GPT 后需要改 BIOS 设置为 UEFI 模式,否则无法启动
GPT 新玩法:绕过 MBR 的限制
超过 2TB 的磁盘:必须用 GPT
# 创建 4TB 分区(MBR 做不到)
sudo parted /dev/sdb
(parted) mklabel gpt
(parted) mkpart primary ext4 0% 100%
(parted) mkfs.ext4 /dev/sdb1多系统引导(UEFI 模式)
UEFI 的 NVRAM 里保存了启动条目列表(BootOrder),每个条目指向一个 .efi 文件的路径。安装多系统时,每个 OS 在 ESP 里安装自己的 .efi,GRUB 可以自动扫描并生成菜单。
# 查看 NVRAM 里的启动条目(efibootmgr)
sudo efibootmgr -v
# 输出示例:
# BootOrder: 0001, 0000
# Boot0001* Arch Linux HD(1,GPT,...)/File(EFIarchgrubx64.efi)
# Boot0000* Windows Boot Manager HD(2,GPT,...)/File(EFIMicrosoftBootbootmgfw.efi)
# 新增一个启动条目
sudo efibootmgr -c -d /dev/sda -p 1 -l "EFIarchgrubx64.efi" -L "Arch Linux"分区名称中文支持
GPT 的 36 字符 UTF-16 名称支持中文,这在服务器管理多硬盘时很有用:
sudo sgdisk --change-name=1:"数据盘-备份" /dev/sda
sudo sgdisk --change-name=2:"系统盘-SSD" /dev/sdb
# 查看
sudo gdisk -l /dev/sda | grep "Partition number"踩坑与注意事项
坑 1:GPT 磁盘在 BIOS 模式下无法启动
现象:磁盘已经换成 GPT,但 BIOS 还是 Legacy 模式,开机直接跳过磁盘。
原因:BIOS/Legacy 模式不认识 GPT(它只看 MBR)。GPT 盘需要 UEFI 模式才能启动。
解决:在 BIOS 设置里切换到 UEFI Only(不开 CSM)。
坑 2:ESP 分区太小(装不下 GRUB 更新)
现象:GRUB 更新时报错 “No space left on ESP”。
原因:ESP 默认 100MB,有些 GRUB 包(特别是带有多个 .efi 变体)会超过 100MB。
解决:重新分区 ESP(至少 300MB):
# 用 gdisk 缩小 ESP(谨慎操作)
# 先备份 ESP 内容,重建后再恢复坑 3:Windows 安装在 MBR 磁盘,克隆到 GPT 磁盘后无法启动
现象:用克隆工具把 MBR 磁盘克隆到 GPT 磁盘,但 Windows 报告 INACCESSIBLE_BOOT_DEVICE。
原因:Windows 的 BCD(Boot Configuration Data)存储在系统分区里,克隆只复制了数据,没有更新 NVRAM 启动条目。
解决:用 Windows PE(WinPE)启动,运行 bcdboot C:Windows /s S: 重建 BCD,或在 UEFI 设置里手动添加启动项。
坑 4:gdisk 转换 MBR 时选错了磁盘
现象:输入 sudo gdisk /dev/sdb,结果 sdb 是另一个磁盘。
原因:Linux 的设备名(sda/sdb)在热拔插时可能变化。
解决:用 lsblk 确认磁盘的 WWN(全球唯一标识)或 UUID:
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,UUID | grep -E "disk|part"
# 或者用
sudo blkid | grep -E "sda|sdb"写在最后
GPT 的本质,是把 MBR 混在一起的三件事(启动代码、分区表、启动标志)拆开了——启动归 ESP(FAT32 分区),分区表归 LBA 2-33 的 16KB 独立区域,校验归 CRC32 + 备份副本。
结果:
- 128 个分区(不需要扩展分区/逻辑分区的 EBR 链了)
- 8ZB 容量上限(64 位 LBA,彻底告别 2TB 天花板)
- 自愈能力:备份 Header + 备份分区表,损坏可恢复
- 分区命名:36 字符 UTF-16,中文友好
- GUID 类型:永不冲突的分区类型标识
wandos 目前没有磁盘驱动,也不支持 GPT 分区解析。如果要做完整的 GPT 支持,需要先实现 NVMe/AHCI 驱动,然后实现 GPT 分区表解析,最后实现 FAT32 ESP 访问——这是教育内核走向”真实磁盘启动”的关键一步。
下篇预告(363):候选方向——VFS:虚拟文件系统层,ext2/fat/proc 文件系统是怎么通过统一接口向用户暴露的;或者 系统调用(0x80):软中断到内核的完整路径。你选哪个?
仓库:https://github.com/golang12306/os-kernel-from-scratch
相关阅读
- Linux Kernel Source:
block/partitions/efi.c(GPT 分区表解析代码) - Linux Kernel Source:
fs/fat/(FAT32 / ESP 文件系统实现) - UEFI Spec: https://uefi.org/specifications(GPT / ESP 规范文档)
- gdisk 源码:
https://www.rodsbooks.com/gdisk/ - https://github.com/zhangfuwen/wandos — Linux 内核教程
动手环节
想深入理解本文内容?动手实践是最好的方式:
今天的目标:用 gdisk 查看你的 GPT 磁盘,备份 GPT Header,然后练习 MBR → GPT 转换。
-
查看你当前磁盘的分区类型:
sudo fdisk -l /dev/sda | grep "GPT" # 输出 "GPT is not present" = MBR,"nGPT is present" = GPT -
如果是 GPT 磁盘,备份 GPT Header 和分区表:
mkdir -p ~/gpt-backup sudo dd if=/dev/sda of=~/gpt-backup/gpt-header.img bs=512 count=1 skip=1 sudo dd if=/dev/sda of=~/gpt-backup/gpt-parttable.img bs=512 count=32 skip=2 ls -la ~/gpt-backup/ -
在测试盘上创建 GPT 分区:
# 找一个测试 U 盘(/dev/sdc) sudo parted /dev/sdc mklabel gpt sudo gdisk /dev/sdc # n → 新建分区 # 起始 → 默认,结束 → +1G(创建 1GB ESP 测试分区) # 类型 → 0700(Microsoft basic data),名称 → "Test ESP" # w → 写入 -
模拟 GPT 损坏恢复:
# 备份后,手动用 dd 写一个错误的 Header(测试用) # 然后用 gdisk 的 "e" 命令从备份恢复 sudo gdisk /dev/sdc # x → 专家模式 # e → 从备份恢复主 Header # v → 验证
提示:操作真实磁盘的 GPT 前,务必备份。GPT Header 损坏虽然可以恢复,但需要正确步骤,盲目操作可能破坏分区表。
评论