3. 分区表原理:MBR与GPT,对齐与挂载

分区表这东西,说白了就是硬盘的“目录索引”。没有它,系统根本不知道数据该往哪写、从哪读。我早年做嵌入式开发时,觉得分区表就是个固定格式,随便分分就行。直到有一次,一个产品因为分区对齐问题,读写性能直接腰斩……嗯,从那以后我再也不敢小看这张“表”了。

3.1 MBR与GPT:两代分区表的核心差异

MBR(主引导记录)诞生于1983年,跟IBM PC DOS 2.0一起出来的。它把分区信息塞在硬盘的第一个扇区(LBA 0),一共只有64字节。你想想看,每个分区表项占16字节,所以最多只能有4个主分区。

MBR的致命短板:

  • 最大支持2.2TB硬盘(32位LBA寻址)
  • 最多4个主分区(或3主分区+1扩展分区)
  • 没有备份,主引导扇区坏了就全完蛋

GPT(GUID分区表)是UEFI时代的产物。它用LBA 1~34存储分区表,LBA 0保留给保护MBR(防止老工具误删)。GPT每个分区表项占128字节,理论上可以分128个分区。我习惯在项目初期就定好GPT方案,尤其是当Flash容量超过2GB时——别笑,很多嵌入式eMMC现在都64GB起步了。

对比项 MBR GPT
最大容量 2.2TB 9.4ZB(实际受OS限制)
主分区数 4个 128个(Windows限制)
备份机制 无 表头和表尾各一份备份
CRC校验 无 分区表头有CRC32
引导方式 BIOS + 活动分区标记 UEFI + ESP分区

注意:有些SoC的BootROM只认MBR。比如某些全志、瑞芯微的芯片,启动时直接读LBA 0的MBR找bootloader。这时候你强行用GPT,芯片根本不理你。我踩过这个坑——当时板子死活起不来,最后发现是分区表格式不对。

3.2 分区对齐:为什么差一个扇区性能差一倍?

分区对齐,指的是分区的起始扇区号必须是某个“对齐单位”的整数倍。这个对齐单位通常是Flash的擦除块大小(eMMC是512KB,NAND一般是128KB~2MB)。

为什么会这样?因为Flash的读写最小单位是页(通常4KB或8KB),但擦除最小单位是块(512KB)。如果分区起始地址不对齐,一个擦除操作可能跨两个物理块,导致写放大——本来只改4KB数据,结果要读、改、写整个512KB块。

我的经验:在嵌入式Linux中,我一般用1MB对齐。为什么是1MB?因为它是512KB(常见eMMC擦除块)的整数倍,也是很多SoC的MMU页大小(4KB)的整数倍。用fdisk创建分区时,加个-I 1048576参数就行。

# 查看当前分区对齐情况
fdisk -l /dev/mmcblk0

# 输出示例(注意Start列)
Device         Start       End   Sectors   Size Type
/dev/mmcblk0p1  2048    206847    204800   100M  EFI System
/dev/mmcblk0p2 206848   2260991   2054144     1G  Linux filesystem

# 2048是1MB对齐(2048 * 512 = 1,048,576)
# 如果看到Start是63或15,说明没对齐,赶紧重分

我曾在项目中遇到过:两块完全一样的eMMC,一块用默认fdisk分区(起始63扇区),一块手动对齐到1MB。跑dd读写测试,对齐的那块顺序写速度是未对齐的2.3倍。你想想看,这差距在批量生产时有多恐怖。

3.3 分区命名规范与挂载点设计

分区命名这事,看似简单,但团队协作时特别容易乱。我见过一个项目,分区名叫system、system_bak、system_old、system_new……最后谁都不知道哪个是当前在用的。

我个人习惯用功能+序号+后缀的命名方式:

分区名 挂载点 文件系统 大小 说明
boot /boot FAT32 64MB 内核+DTB,UEFI/uboot读取
rootfs / ext4 2GB 根文件系统
rootfs_bak (不挂载) ext4 2GB OTA升级备用
data /data ext4 剩余 用户数据,可读写
cache /cache ext4 256MB 系统缓存,可随时清
misc (不挂载) raw 1MB 启动模式、恢复标志

挂载点设计原则:

  • 只读分区尽量多:rootfs、boot设为只读挂载,防止意外写坏
  • 可写分区单独分:/data、/cache独立,方便OTA时只擦除rootfs
  • 临时分区用tmpfs:/tmp、/run放内存里,减少Flash磨损
  • 日志分区要小心:ext4的journal会频繁写,我一般用noatime挂载

我曾经在一个车载项目里,因为没把日志分区独立出来,结果rootfs的ext4 journal把Flash写穿了——才跑了3个月,eMMC就报重映射块过多。后来我把/var/log单独分了一个区,用f2fs文件系统,问题就解决了。

3.4 一张图看懂分区表与启动流程

下面这张图,把MBR/GPT、分区对齐、挂载点设计串在一起。你可以看到,从SoC上电到挂载根文件系统,每一步都跟分区表息息相关。

分区表与系统启动流程 分区表与系统启动加速流程 SoC BootROM 读取LBA 0 (MBR/GPT) MBR:4主分区 GPT:128分区 分区对齐检查(1MB对齐) 未对齐 → 写放大,性能腰斩 挂载分区 /boot (FAT) → / (ext4) → /data (ext4) 系统启动完成 关键加速点 ① 分区表放在LBA 0~34 ② 分区起始1MB对齐 ③ 只读分区减少写磨损 ④ 独立cache分区加速IO 避坑提醒 • 别用MBR分超过2TB盘 • 别把日志放rootfs • 别忘记备份GPT表尾

3.5 实战:用sgdisk快速创建对齐分区

我推荐用sgdisk替代fdisk,尤其是在GPT场景下。它支持脚本化操作,适合批量生产。

# 创建一个4GB镜像,1MB对齐
dd if=/dev/zero of=disk.img bs=1M count=4096

# 用sgdisk分区
sgdisk -n 1:2048:206847 -t 1:ef00 -c 1:"boot" \
       -n 2:206848:2260991 -t 2:8300 -c 2:"rootfs" \
       -n 3:2260992: -t 3:8300 -c 3:"data" \
       disk.img

# 验证对齐
sgdisk -i 1 disk.img
# 输出:Partition GUID code: ... (EFI System)
#       Partition starts at 2048 (对齐到1MB)

小技巧:在嵌入式设备上,我习惯把sgdisk命令写到烧录脚本里。这样产线烧录时,分区表、对齐、命名全部自动化,不会有人为失误。我曾经见过产线工人手动输错分区起始扇区,导致整批板子返工——那场面,嗯,不想再经历第二次。

分区表这东西,平时看不见摸不着,但一旦出问题,就是启动失败、性能暴跌、数据丢失。花10分钟把分区表规划好,能省下后面10小时的调试时间。我个人习惯在项目启动阶段,就把分区表方案写到设计文档里,团队评审通过后再动手。别嫌麻烦——你想想看,等产品量产了再改分区表,那才叫真麻烦。