从零写OS内核 | MBR 分区:磁盘是怎么被切成块的,以及多系统怎么共存
你有没有过这种经历:买了一块新硬盘,插进电脑,打开磁盘管理工具,看到的是一片黑屏,上面写着”未分配”——然后你右键”新建简单卷”,选了大小,点下去,硬盘就被切成了几块——C盘、D盘、E盘。
这个”切硬盘”的操作,就是分区。它是怎么工作的?为什么一块物理硬盘可以同时装 Windows 和 Linux?MBR 是什么?为什么分区表要放在磁盘最前面那个扇区里?为什么超过 2TB 的硬盘不能用在 MBR 模式?
今天,我们来拆解 MBR 分区的完整原理——从数据结构的每一个字节,到多系统共存的实战操作。
MBR 的物理结构:512 字节里装了什么
MBR(Master Boot Record)不是一个分区,是一个启动记录。它固定在磁盘的第一个扇区(LBA 0),占 512 字节。这 512 字节里,只有前 446 字节是代码,后面 64 字节是分区表,最后 2 字节是启动标志。
MBR 512 字节布局(LBA 0):
┌─────────────────────────────────────────┐
│ 0x0000 - 0x01BD(446 字节):Bootloader 代码区 │
│ (GRUB Stage 1 就放在这里,或者 Windows 的 BOOTMGR)│
├─────────────────────────────────────────┤
│ 0x01BE - 0x01FD(64 字节):分区表(4 条×16 字节) │
│ 第 1 条:0x01BE - 0x01CD │
│ 第 2 条:0x01CE - 0x01DD │
│ 第 3 条:0x01DE - 0x01ED │
│ 第 4 条:0x01EE - 0x01FD │
├─────────────────────────────────────────┤
│ 0x01FE - 0x01FF(2 字节):0x55 0xAA(启动标志) │
└─────────────────────────────────────────┘用 hexdump 实际看一下你的磁盘 MBR:
# 读取第一块硬盘的 MBR(不需要 root,只要能读 /dev/sda)
dd if=/dev/sda bs=1 count=512 2>/dev/null | hexdump -C | head -30
# 输出示例(部分):
# 00000000 eb 63 90 00 00 00 00 00 00 00 00 00 00 00 00 00 |.c..............|
# ... boot code ...
# 000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
# 000001c0 01 00 07 fe ff ff 3f 00 00 00 d8 63 19 00 00 00 |.......?...c....|
# 000001d0 80 01 05 0f fe ff ff 57 d8 63 19 00 f8 7f 19 00 |......W.c.......|
# 000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
# 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
# ^^^^
# 启动标志 0x55AA如果你用的是 Linux,MBR 的 bootloader 代码区可能被 GRUB 占用;如果是 Windows 原装系统,这里通常是 BOOTMGR(Windows Boot Manager)。
分区表结构:64 字节怎么描述一个分区
分区表的每条记录是 16 字节,描述一个分区的基本信息:
分区表条目结构(16 字节):
┌────────┬──────┬──────────────────────────────────────────────────┐
│ 偏移 │ 大小 │ 含义 │
├────────┼──────┼──────────────────────────────────────────────────┤
│ 0x00 │ 1B │ 分区状态(0x80=可启动,0x00=不可启动) │
│ 0x01 │ 1B │ 起始磁头号(CHS 寻址,已过时,但字段保留) │
│ 0x02 │ 2B │ 起始扇区/柱面(CHS,低 6 位是扇区,高 2 位+下一字节是柱面)│
│ 0x04 │ 1B │ 分区类型(0x07=NTFS,0x0B=FAT32,0x83=Linux,0xEE=EFI)│
│ 0x05 │ 1B │ 结束磁头号(CHS) │
│ 0x06 │ 2B │ 结束扇区/柱面(CHS) │
│ 0x08 │ 4B │ 起始扇区号(LBA,32 位,**MBR 最大只支持 2TB**) │
│ 0x0C │ 4B │ 分区总扇区数(32 位) │
└────────┴──────┴──────────────────────────────────────────────────┘关键字段解释:
-
可启动标志(0x80):4 条分区里只能有 1 条是 0x80,告诉 BIOS”这个分区有启动代码”。这就是为什么 C 盘通常是可启动的,而 D、E 盘不是。
-
分区类型(0x04-0x05):不是给操作系统看的,是给工具软件看的。常见类型码:
0x07:NTFS(Windows)0x0B/0x0C:FAT32(U 盘、ESP 分区)0x83:Linux Native(ext4/XFS)0x82:Linux Swap0xEE:GPT Protective(表示”这个磁盘是 GPT 格式,别用 MBR 解释”)0xEF:EFI System Partition
-
起始扇区 + 总扇区数:用 LBA(Logical Block Addressing)记录,不依赖 CHS 几何结构。现代磁盘都用 LBA 寻址。
CHS 寻址的局限
早期的磁盘用 CHS(Cylinder/Head/Sector)寻址——柱面号、磁头号、扇区号三个参数定位一个扇区。这种方式有天花板:最多 8GB(1024 柱面 × 255 磁头 × 63 扇区 × 512 字节)。超过 8GB 的硬盘,用 CHS 根本找不到后面的扇区。
MBR 分区表保留了 CHS 字段(兼容老系统),但实际使用的是 LBA 字段(偏移 0x08 和 0x0C 的 4 字节值)。这就是为什么同一个分区,工具有时显示 CHS,有时显示 LBA——它们用的是同一个数据的不同解释。
MBR 的 2TB 上限
MBR 用 32 位记录扇区号,每个扇区 512 字节,所以最大支持:
2^32 × 512 = 2.199 TB(实际可寻址 2.00 TB,因为 2^32 = 4294967296)超过 2TB 的磁盘,必须用 GPT 格式——MBR 无法描述那么大的空间。这也是为什么大硬盘、安装 Windows 10/11 的机器,99% 都用了 UEFI + GPT,不是因为 GPT 更先进,是因为 MBR 物理上限就在这里。
分区类型:逻辑上怎么理解”扩展分区”
MBR 分区表只有 4 条记录——这是 MBR 的最大限制:最多 4 个主分区。
但 4 个不够用怎么办?答案是:扩展分区。
原理是这样的:把第 4 条分区记录标记为”扩展分区”(类型 0x0F,或老式的 0x05),它不是一个真正的数据分区,而是一个”容器”——指向另一个结构(EBR,Extended Boot Record),里面又可以切出多个”逻辑分区”。
MBR 分区布局(典型 4 主分区 + 逻辑分区):
┌─────────────────────────────────────────────────┐
│ MBR(4 条分区表) │
│ 条目 1:/dev/sda1(主分区,Windows C:) │
│ 条目 2:/dev/sda2(主分区,Linux root) │
│ 条目 3:/dev/sda3(扩展分区,指向 EBR 链) │
│ 条目 4:空 │
└─────────────────────────────────────────────────┘
↓(扩展分区起始扇区放 EBR)
┌─────────────────────────────────────────────────┐
│ EBR 1(描述逻辑分区 D:) → 指向 EBR 2 │
│ EBR 2(描述逻辑分区 E:) → 指向(无,结束) │
└─────────────────────────────────────────────────┘EBR 的结构和 MBR 几乎一样,但含义不同:
- EBR 里的第一个分区条目:描述当前的逻辑分区(起始扇区相对于扩展分区的起始位置)
- EBR 里的第二个分区条目:指向下一个 EBR 的位置(下一个逻辑分区的 EBR 扇区号)
- 第三个、第四个条目:永远是 0(EBR 不支持嵌套)
这样,逻辑上就可以有无限多个逻辑分区(实际上受磁盘总大小限制)。Linux 的 fdisk 默认帮你做这件事,你不需要手动管理 EBR。
MBR 工具:Linux 上的分区工具链
fdisk:最经典,交互友好
# 查看磁盘分区表(不修改任何数据)
sudo fdisk -l /dev/sda
# 输出示例:
# Disk /dev/sda: 238.47 GiB, 256000000000 bytes, 500000000 sectors
# Units: sectors of 1 * 512 = 512 bytes
# Sector size (logical/physical): 512 bytes / 512 bytes
# I/O size (minimum/optimal): 512 bytes / 512 bytes
#
# Device Boot Start End Sectors Size Id Type
# /dev/sda1 * 2048 1230847 1228800 600M 83 Linux
# /dev/sda2 1230848 62504959 61274112 29.2G 83 Linux
# /dev/sda3 62504960 500117503 437612544 208.7G 5 Extended
# /dev/sda5 62505088 500117503 437612416 208.7G 83 Linux注意 /dev/sda5 开始是 5——这是因为 /dev/sda3 是扩展分区(容器),逻辑分区从 5 开始编号。主分区 1-4,逻辑分区 5+。
cfdisk:更友好的交互式工具
sudo cfdisk /dev/sda
# 用方向键操作,界面比 fdisk 更直观
# 新建 / 删除 / 调整大小 分区,直接操作,不用记命令parted:支持超过 2TB 的磁盘,支持 GPT
# parted 默认是交互式的
sudo parted /dev/sda
# 或者一行命令完成(创建 GPT 分区)
sudo parted /dev/sdb mklabel gpt
sudo parted /dev/sdb mkpart primary ext4 0% 100%用 dd 备份和恢复 MBR
备份(整个 MBR,包括分区表):
# 备份 MBR(446 字节 bootloader + 64 字节分区表 + 2 字节标志 = 512 字节)
sudo dd if=/dev/sda of=/mnt/backup/mbr-backup.img bs=512 count=1
# 只备份分区表(不含 bootloader)
sudo dd if=/dev/sda of=/mnt/backup/partition-table.img bs=1 count=64 skip=446恢复(注意:恢复分区表不会删除数据,但如果 bootloader 被覆盖会导致无法启动):
# 恢复整个 MBR
sudo dd if=/mnt/backup/mbr-backup.img of=/dev/sda bs=512 count=1
# 恢复分区表(谨慎操作)
sudo dd if=/mnt/backup/partition-table.img of=/dev/sda bs=1 count=64 seek=446多系统共存:Windows + Linux 在同一块盘上
核心原理:bootloader 不是操作系统的专利
多系统共存的核心是:每个操作系统都认为自己”拥有”整块磁盘——但实际上,每个 OS 只能读写自己的分区。Bootloader(GRUB 或 Windows BOOTMGR)才是多系统共存的粘合剂。
磁盘分区结构(典型 Windows + Linux 双系统):
┌──────────────────────────────────────────────────────┐
│ /dev/sda1:Windows(C:,NTFS,0x07,启动标志=0x80) │
│ /dev/sda2:Linux root(ext4,0x83) │
│ /dev/sda3:Linux swap(0x82) │
│ /dev/sda4:扩展分区(0x0F),包含逻辑分区 │
│ /dev/sda5:/home(ext4,逻辑分区) │
└──────────────────────────────────────────────────────┘
MBR bootloader 代码区:GRUB Stage 1(446 字节)
GRUB Stage 2:/dev/sda2 的 /boot/grub/(在 Linux 分区里)
启动流程:
BIOS → MBR(GRUB Stage 1) → 读 /dev/sda2 的 /boot/grub/ → grub.cfg
→ 显示菜单(Windows 在第一条,Linux 在第二条)→ 用户选谁跳谁安装顺序:谁先谁后有讲究
先装 Windows,后装 Linux(推荐):
- Windows 先装,占用 C 盘
- 安装 Linux 时,GRUB 会自动检测到 Windows,在 grub.cfg 里生成”Windows Boot Manager”条目
- GRUB 写到 MBR,覆盖 Windows 的 BOOTMGR
- 启动时 GRUB 菜单会显示两个选项
先装 Linux,后装 Windows(麻烦):
- Linux 先装,GRUB 写到 MBR
- Windows 安装时会把 MBR 覆盖成自己的 BOOTMGR——GRUB 消失
- 恢复方法:Linux Live USB 启动,运行
grub-install /dev/sda
用 GRUB 手动添加 Windows 启动条目
如果 Windows 没有被 GRUB 检测到,手动添加:
# 进入 GRUB 命令行(在菜单按 c)
grub> insmod ntfs
grub> set root='hd0,msdos1' # 第一块硬盘第一个主分区(Windows)
grub> chainloader +1 # 加载这个分区的第一个扇区(BOOTMGR)
grub> boot # 启动 Windows或者修改 /boot/grub/grub.cfg(永久有效):
menuentry 'Windows 10' {
insmod ntfs
set root='hd0,msdos1'
chainloader +1
}修复 GRUB(最常见的双系统问题)
症状:重装 Windows 后,GRUB 消失,开机直接进 Windows(或黑屏)。
原因:Windows 安装程序会覆盖 MBR,不认其他 OS 的 bootloader。
修复步骤:
# 1. 用 Linux Live USB 启动(Arch/ Ubuntu 安装盘都可以)
# 2. 找到 Linux 分区
sudo fdisk -l /dev/sda
# 假设 Linux 在 /dev/sda2
# 3. 挂载 Linux 分区
sudo mount /dev/sda2 /mnt
sudo mount --bind /dev /mnt/dev
sudo chroot /mnt
# 4. 重新安装 GRUB 到 MBR
grub-install /dev/sda
update-grub # 生成 grub.cfg(自动检测 Windows)
# 5. 重启
exit
sudo rebootwandos 的分区支持:现状
wandos 是一个教育用内核,暂时没有文件系统层面的分区支持——它假设 bootloader(GRUB)已经完成了分区解析,直接从某个分区加载内核镜像。
wandos 的 boot 代码里,如果要支持从特定分区读取数据,需要:
- 通过 GRUB 传入的
boot_info结构体,读取内存映射(mmap_*字段)找到分区位置 - 实现一个简化的 ATA/PATA 或 NVMe 驱动来访问磁盘
- 实现 FAT32/ ext2 解析器来读文件
目前 wandos 的 drivers/ 下没有磁盘驱动,这也是为什么 wandos 演示用的是 QEMU 的 -kernel 选项(绕过磁盘,直接加载 ELF),而不是真实从磁盘启动。
差距说明:wandos 没有自己的分区表解析和磁盘驱动。如果要做完整的”从磁盘启动”支持,需要先实现 drivers/storage/(ATA/SATA/NVMe 驱动)+ fs/(FAT32 解析)+ boot/(MBR 解析)。这是一整个子系统的工作量。
Linux 实践:亲手操作 MBR
查看 MBR 十六进制内容
# 查看 /dev/sda 的 MBR(512 字节 = 32 行 × 16 字节)
sudo dd if=/dev/sda bs=1 count=512 2>/dev/null | hexdump -C | head -35
# 重点看分区表部分(从 0x01BE 开始)
sudo dd if=/dev/sda bs=1 skip=446 count=64 2>/dev/null | hexdump -C
# 每条分区 16 字节,分隔清晰
# 条目 1:0x01BE - 0x01CD
# 条目 2:0x01CE - 0x01DD
# 条目 3:0x01DE - 0x01ED
# 条目 4:0x01EE - 0x01FD查看每个分区的类型
# 用 fdisk 列出分区类型
sudo fdisk -l /dev/sda | grep -E "Device|/dev"
# 或者用 parted(更详细)
sudo parted /dev/sda print
# 查看文件系统类型
lsblk -f
# NAME FSTYPE LABEL UUID
# sda1 ntfs Windows C:...
# sda2 ext4 root
# sda5 ext4 home创建一个新分区(实验用)
# 强烈警告:只在测试盘上操作,不要动你的系统盘!
# 用 fdisk 创建新分区
sudo fdisk /dev/sdb
# 交互式命令:
# n → 新建分区
# p → 主分区(e → 扩展分区)
# 分区号 → 默认
# 起始扇区 → 默认
# 结束扇区 → +10G(创建 10GB 分区)
# w → 写入保存
# 写入后,创建文件系统
sudo mkfs.ext4 /dev/sdb1
# 挂载测试
sudo mount /dev/sdb1 /mnt/test
df -h /mnt/test踩坑与注意事项
坑 1:MBR 损坏导致”无启动设备”错误
现象:开机显示 “No bootable device”,BIOS 可以识别磁盘但找不到启动代码。
原因:MBR 的 0x55AA 标志被破坏(可能是工具操作失误),或者 MBR 的 boot code 区被意外清空。
解决:用 boot-repair 工具修复,或手动 dd 恢复备份的 MBR:
# 用 Arch Linux Live USB
sudo dd if=/path/to/mbr-backup.img of=/dev/sda bs=512 count=1坑 2:MBR 分区容量计算错误(超过 2TB 部分不可见)
现象:4TB 硬盘,在 Windows 的磁盘管理里只能看到 2TB,剩余 2TB 显示”未分配”但无法使用。
原因:Windows 用 MBR 模式安装,分区表是 32 位的,只能寻址 2TB。
解决:转换为 GPT 模式(会丢失所有数据):
# Linux 下(不会丢数据,但需要备份)
sudo parted /dev/sda mklabel gpt
# 然后重建分区坑 3:修复 Windows 后 GRUB 消失(最常见)
现象:重装 Windows 后,GRUB 菜单没了,Linux 无法启动。
原因:Windows 安装程序会覆盖 MBR(446 字节的 BOOTMGR 代码)。
解决:上面已描述,用 Linux Live USB + grub-install 修复。
坑 4:扩展分区和逻辑分区混淆
现象:用 fdisk 删除扩展分区后,逻辑分区全部消失,但数据实际还在(只是分区表损坏)。
原因:扩展分区是逻辑分区的”容器”,删除容器,里面的东西从分区表视角消失,但磁盘上的数据还在。用 testdisk 可以恢复。
# 用 testdisk 恢复丢失的分区
sudo apt install testdisk
sudo testdisk
# 选"分析"→ 选磁盘 → 搜索丢失的分区 → 写入写在最后
MBR 是磁盘第一个扇区里的 512 字节——446 字节是 bootloader,64 字节是分区表(4 条记录),2 字节是启动标志。分区表描述的是”这块磁盘被切成哪几块,每块从哪开始、有多大、是什么类型”。
MBR 的局限:最多 4 个主分区、最大 2TB 容量——这两个限制都是因为 16 字节的分区记录 + 32 位的扇区号,不是设计失误,是 1983 年制定标准时的权衡。
多系统共存的关键,不是”每个 OS 各管各的”,而是 GRUB 作为 bootloader 统一管理启动顺序——它读取每个分区的启动扇区,依次把控制权交给各个 OS。
wandos 没有磁盘驱动,所以暂时依赖 GRUB 的 Multiboot 传递磁盘信息。要实现完整的 MBR 解析能力,需要从 drivers/storage/ 做起。
下篇预告(362):候选方向——/proc 和 /sys 虚拟文件系统(内核信息的窗口,进程信息、硬件状态是怎么暴露给用户态的),或者 系统调用:软中断到内核的完整路径(从 0x80 陷阱到 syscall_table 的寻址)。你选哪个?
仓库:https://github.com/golang12306/os-kernel-from-scratch
相关阅读
- Linux Kernel Source:
block/partitions/(MBR、GPT、BSD 分区解析代码) - Linux Kernel Source:
fs/ext4/super.c(ext4 分区挂载过程) - GRUB Manual: https://www.gnu.org/software/grub/manual/grub/
- fdisk 源码:
util-linux/libfdisk/(MBR 分区表读写实现) - https://github.com/zhangfuwen/wandos — Linux 内核教程
动手环节
想深入理解本文内容?动手实践是最好的方式:
今天的目标:用 dd + hexdump 自己读 MBR,理解分区表的二进制结构,然后练习多系统修复。
-
查看你电脑的 MBR:
sudo dd if=/dev/sda bs=1 count=512 2>/dev/null | hexdump -C | head -35 # 数一数分区表从哪个字节开始(0x01BE) # 读出前 4 条分区记录,看看你的系统是怎么分区的 -
备份 MBR(重要):
sudo dd if=/dev/sda of=/home/$USER/mbr-backup.img bs=512 count=1 ls -la /home/$USER/mbr-backup.img # 应该显示 512 字节 -
在测试盘上实验(不要动系统盘):
# 找一个测试用的 U 盘或移动硬盘(/dev/sdb) sudo fdisk /dev/sdb # 创建两个主分区:一个 FAT32,一个 ext4 # 用 hexdump 读 MBR,对照分区表结构理解 -
修复 GRUB 练习:
在虚拟机里安装 Windows + Linux(或者反过来),模拟”Windows 重装后 GRUB 消失”,然后用 Live USB 修复。
提示:操作真实磁盘前,务必备份 MBR。MBR 损坏比分区丢失更难恢复——前者影响整个磁盘的启动能力,后者只是丢失分区表(数据还在)。
评论