3. 分区表概念:分区表的作用、常见分区表格式(GPT、MBR)、分区属性

分区表这东西,说白了就是一张“地图”。

你想想看,一块Flash或者硬盘,少则几MB,多则几十GB。Bootloader、内核、文件系统、用户数据全堆在上面。如果没有分区表,你怎么知道哪个区域是干什么的?

分区表就是用来干这个的。它告诉Bootloader:“从哪个地址开始,到哪个地址结束,这块区域归谁管”。

3.1 分区表到底有什么用?

我刚开始做嵌入式的时候,觉得分区表就是个“形式主义”。反正代码写死了地址,直接跳过去不就行了?

后来项目量产,发现不同批次Flash的坏块分布不一样,固件升级又需要动态调整分区大小。嗯,从那以后我再也不敢硬编码地址了。

分区表的核心作用,我总结为三点:

  • 地址映射:把逻辑分区名称(如“kernel”、“rootfs”)映射到物理存储的起始地址和大小。
  • 启动引导:Bootloader通过分区表找到下一个启动阶段(如U-Boot、ATF、内核)的位置。
  • 空间管理:防止不同模块越界写入,避免“你踩了我的内核,我占了你的文件系统”。

一句话总结:没有分区表的系统,就像没有门牌号的楼房——你知道里面有人,但不知道谁住在哪一间。

3.2 两种主流分区表格式:MBR vs GPT

嵌入式领域,最常见的就是MBR和GPT。我分别说说它们的脾气。

3.2.1 MBR(Master Boot Record)

MBR是“老前辈”了。它诞生于1980年代,最初是为x86 PC设计的。结构很简单:

  • 占用存储设备的第一个扇区(通常是512字节)。
  • 前446字节是启动代码(Bootloader的第一阶段)。
  • 接着64字节是分区表,最多记录4个主分区。
  • 最后2字节是魔数(0x55AA),用于校验。

MBR的局限性很明显:

  • 最大支持2TB:因为分区地址用32位表示,每个扇区512字节,算下来就是2^32 × 512 = 2TB。
  • 最多4个主分区:想多分?得用扩展分区和逻辑分区,麻烦得很。
  • 没有备份:MBR只存在第一个扇区,一旦损坏,整个盘就废了。

我曾经踩过的坑:在一个NAND Flash项目里,我用了MBR。结果Flash前几个块坏了,MBR直接读不出来。整个系统变砖。后来我强制要求:所有新项目必须用GPT,或者至少保留MBR的冗余备份。

3.2.2 GPT(GUID Partition Table)

GPT是MBR的“接班人”。它是UEFI规范的一部分,现在基本是主流。

GPT的优势很明显:

  • 支持超大容量:用64位表示LBA地址,理论上支持8ZB(对,你没看错,泽字节级别)。
  • 分区数量几乎无限制:默认预留128个分区表项,不够还能扩展。
  • 有备份:GPT在存储设备的头部和尾部各存一份分区表,一份坏了,另一份顶上。
  • 自带校验:每个分区表项都有CRC32校验,防止数据损坏。

但GPT也不是没缺点。它占用的空间比MBR大——头部需要至少34个扇区(包括主分区表和备份分区表)。对于只有几MB的Flash来说,这个开销有点心疼。

对比项 MBR GPT
诞生时间 1980年代 2000年代(UEFI规范)
最大容量 2TB 8ZB
最大主分区数 4个 128个(默认)
备份机制 无 头部+尾部双备份
校验机制 仅魔数0x55AA CRC32校验
适用场景 小容量、老旧系统 大容量、新设计

我的个人习惯:对于Flash小于16MB的MCU项目,我倾向于用自定义分区表(类似MBR但更精简)。对于Linux+大容量eMMC/NAND的项目,我无脑选GPT。别问为什么,问就是省心。

3.3 分区属性:每个分区都有“身份证”

不管是MBR还是GPT,每个分区表项都包含一组属性。我挑几个最重要的说说。

3.3.1 起始地址(Start LBA)

这个字段告诉Bootloader:“这个分区从哪个扇区开始”。

在MBR里,它是32位的,单位是扇区。在GPT里,它是64位的。

注意:起始地址必须是扇区对齐的。我见过有人把起始地址设成奇数扇区,结果读写性能直接砍半。

3.3.2 大小(Size / End LBA)

分区的大小。MBR用“起始地址 + 扇区数”表示,GPT用“起始LBA + 结束LBA”表示。

这里有个细节:结束LBA是包含的。也就是说,分区占用的扇区范围是 [StartLBA, EndLBA]。

3.3.3 类型(Partition Type)

这个字段标识分区里装的是什么。

  • 在MBR里,类型是一个1字节的ID。比如0x83表示Linux文件系统,0x0C表示FAT32,0x82表示Swap。
  • 在GPT里,类型是一个128位的GUID。比如Linux文件系统的GUID是 0FC63DAF-8483-4772-8E79-3D69D8477DE4。

我个人觉得,类型字段最大的作用是防止误用。Bootloader读到分区类型后,可以判断“这个分区是不是我能启动的”。比如你让Bootloader去加载一个Swap分区,那肯定崩。

3.3.4 标志位(Flags / Attributes)

标志位是分区的“开关”和“属性”。

  • MBR:每个分区表项有一个字节的标志位。最常用的是第7位(0x80),表示“可引导分区”。
  • GPT:每个分区表项有64位的属性字段。常用的位包括:
    • Bit 0:Required(系统必需分区,不能删除)
    • Bit 1:No Block IO(不通过块IO访问)
    • Bit 2:Legacy BIOS Bootable(兼容传统BIOS启动)

避坑指南:我曾经在调试一个双系统启动时,发现第二个系统死活起不来。查了两天,最后发现是MBR里两个分区都标记了“可引导”。Bootloader只认第一个,第二个直接被忽略。所以,标志位不是你想设就能设的,得跟Bootloader的解析逻辑对齐。

3.4 一张图看懂分区表结构

说了这么多,不如直接画张图。下面这张SVG展示了GPT分区表的完整布局,以及Bootloader如何通过它找到各个分区。

GPT 分区表结构及启动链 存储设备(eMMC / NAND / SSD) GPT 头部(LBA 0) 分区表项 1..128 备份 GPT(尾部) Bootloader (LBA 34~100) ATF / SPL (LBA 101~200) Kernel (LBA 201~500) Rootfs (LBA 501~2000) User Data (LBA 2001~) 启动链:Bootloader → 读取GPT头部 → 解析分区表项 → 找到Kernel分区 → 加载并跳转

3.5 嵌入式场景下的分区表选择建议

说了这么多理论,最后给点实际建议。

  • 小Flash(≤16MB):用自定义精简分区表,或者MBR。GPT的头部开销太大,不划算。
  • 大容量eMMC/NAND(≥1GB):无脑上GPT。备份和校验功能太香了。
  • 需要兼容老旧Bootloader:查一下Bootloader的源码,看它支持MBR还是GPT。有些U-Boot版本对GPT支持不完整。
  • 安全启动场景:建议用GPT,因为可以在分区属性里加“只读”或“已验证”标志,防止分区被篡改。

我的经验:不管用哪种格式,一定要在分区表里预留一个“备用分区”。OTA升级时,新固件先写到备用分区,验证通过后再切换。这样即使升级失败,系统还能回滚。这个习惯救了我好几次。