分区表基础:分区表的作用、MBR与GPT分区表格式、分区类型与属性

各位做嵌入式开发的同行,今天我们来聊聊分区表。这东西看着不起眼,但你要是搞错了,板子直接变砖。我这些年调试过的Bootloader问题,少说有三成跟分区表有关。所以这一章,咱们把基础打扎实。

分区表到底是干什么的?

说白了,分区表就是一张地图。它告诉Bootloader:你的Flash或者eMMC上,哪块区域是Bootloader自己,哪块是内核,哪块是文件系统,哪块是用户数据。

没有分区表,Bootloader启动时就像没头苍蝇——它不知道从哪里加载内核,也不知道该跳转到哪个地址。你想想看,一个系统连自己身体结构都不清楚,怎么可能跑得起来?

核心作用一句话:分区表是Bootloader与存储介质之间的契约,定义了每个分区的起始地址、大小、类型和属性。

我在一个量产项目上遇到过这种情况:工程师把分区表偏移搞错了1个扇区,结果Bootloader每次启动都读到错误的内核镜像,系统反复重启。查了三天,最后发现是分区表里一个字节写错了。嗯,从那以后我每次写分区表都要反复对三遍。

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

嵌入式领域最常见的就是MBR和GPT。我建议你至少把这两种都吃透,因为不同芯片、不同项目用的格式不一样。

MBR(Master Boot Record)

MBR是上世纪80年代的老将了,但至今仍在大量使用。它位于存储设备的第一个扇区(LBA 0),总共512字节。结构很简单:

  • 引导代码(446字节):Bootloader的第一阶段代码,负责加载后续阶段
  • 分区表(64字节):4个分区表项,每个16字节
  • 结束标志(2字节):固定为0x55AA

每个分区表项包含:

偏移 长度 含义
0x00 1字节 引导标志(0x80表示可引导)
0x01 3字节 起始CHS地址
0x04 1字节 分区类型(如0x83=Linux, 0x0C=FAT32)
0x05 3字节 结束CHS地址
0x08 4字节 起始LBA地址
0x0C 4字节 分区大小(扇区数)

注意:MBR最大只能管理2TB的磁盘,因为LBA地址用32位表示。另外,MBR只支持4个主分区。如果需要更多分区,就得用扩展分区和逻辑分区——这在嵌入式里很麻烦。

我曾经调试过一个项目,Flash容量刚好超过2TB,结果MBR分区表死活认不全。后来换成GPT,问题立刻解决。所以,如果你的存储介质超过2TB,别犹豫,直接上GPT。

GPT(GUID Partition Table)

GPT是UEFI时代的产物,也是现代嵌入式系统的首选。它使用LBA 1~33来存放分区表信息,结构更灵活、更健壮。

GPT的核心结构:

  • 保护MBR(LBA 0):一个假的MBR,防止老工具误操作
  • GPT头(LBA 1):包含分区表的位置、大小、校验和等信息
  • 分区表项(LBA 2~33):每个分区表项128字节,最多支持128个分区
  • 备份GPT(磁盘末尾):主GPT的完整备份,用于恢复

每个GPT分区表项包含:

偏移 长度 含义
0x00 16字节 分区类型GUID
0x10 16字节 唯一分区GUID
0x20 8字节 起始LBA地址
0x28 8字节 结束LBA地址
0x30 8字节 属性标志
0x38 72字节 分区名称(UTF-16LE)

个人建议:新项目一律用GPT。它支持2TB以上容量、无限分区数(实际受限于表项数量)、有CRC校验防止数据损坏,还有备份GPT做容错。我在一个车机项目里用GPT,有一次Flash坏块导致主GPT损坏,Bootloader自动切换到备份GPT,系统照样启动——这种可靠性,MBR给不了。

分区类型与属性

分区类型决定了这个分区里装的是什么。Bootloader根据分区类型来决定如何处理这个分区。

常见分区类型

类型 MBR类型值 GPT类型GUID 用途
未使用 0x00 00000000-0000-0000-0000-000000000000 空表项
FAT12 0x01 EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 小容量文件系统
FAT16 0x06 EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 旧式文件系统
FAT32 0x0C EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 通用文件系统
Linux 0x83 0FC63DAF-8483-4772-8E79-3D69D8477DE4 Linux原生分区
Linux Swap 0x82 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F 交换分区
EFI系统 0xEF C12A7328-F81F-11D2-BA4B-00A0C93EC93B EFI引导分区
Bootloader 自定义 自定义 存放Bootloader镜像

分区属性

分区属性决定了Bootloader怎么对待这个分区。常见的属性包括:

  • 启动标志:MBR里用0x80标记可引导分区,GPT里用属性位表示。Bootloader会扫描所有分区,找到带启动标志的那个,然后加载它。
  • 系统分区:存放操作系统内核和核心文件。Bootloader通常需要从这个分区加载内核镜像。
  • 数据分区:存放用户数据、日志、配置等。Bootloader一般不需要直接访问,但有些场景下需要从数据分区读取升级包。
  • 只读属性:防止意外写入。我在一个安全关键系统里,把Bootloader分区设为只读,结果有一次升级程序写错了地址,差点把Bootloader覆盖掉——幸好只读属性拦住了。

关键点:Bootloader在启动时,会按照分区表里的顺序扫描每个分区。它先看分区类型,再看属性标志。如果分区类型是"Bootloader"且属性包含"可执行",Bootloader就会尝试加载并跳转。如果分区类型是"系统",Bootloader就会加载内核。

分区表在Bootloader中的典型布局

我画了一张图,展示一个典型的嵌入式系统分区布局。你可以看到分区表在整个存储结构中的位置。

嵌入式系统分区布局图 嵌入式系统典型分区布局 存储介质(Flash / eMMC) LBA 0:保护MBR(仅GPT使用) LBA 1:GPT头(分区表位置、大小、校验和) LBA 2 ~ LBA 33:分区表项(128个分区表项) 每个表项128字节,包含类型GUID、起始地址、大小、属性、名称 数据区域(分区内容) Bootloader分区 | 系统分区 | 数据分区 | 升级分区 每个分区由分区表项定义起始地址和大小 磁盘末尾:备份GPT(容错恢复用) GPT布局示意

这张图展示的是GPT的布局。如果是MBR,结构更简单:LBA 0就是MBR本身,包含引导代码和4个分区表项,没有备份区域。

Bootloader如何解析分区表

Bootloader启动时,分区表解析流程大致如下:

  1. 读取LBA 0:判断是MBR还是GPT(通过检查0x55AA标志和后续结构)
  2. 如果是MBR:直接解析4个主分区表项,读取类型、起始地址、大小
  3. 如果是GPT:先读LBA 1的GPT头,获取分区表项的位置和数量,再读分区表项
  4. 遍历分区:按顺序扫描所有分区,匹配类型和属性
  5. 加载目标分区:找到启动分区或系统分区后,读取内容到内存并执行

避坑指南:我曾经在一个项目里发现,Bootloader解析GPT时没有校验CRC。结果有一次Flash写入错误,GPT头损坏,Bootloader读到了错误的分区表项,直接跳转到非法地址,系统崩溃。后来我加上了CRC校验,并在校验失败时自动切换到备份GPT——这个改动救了那个项目至少三次。

分区表调试常见问题

最后,我总结几个我在调试中经常遇到的分区表问题:

  • 分区表偏移错误:分区表没有放在正确的LBA地址。Bootloader读到的全是垃圾数据。
  • 分区大小计算错误:分区大小用字节数而不是扇区数,或者扇区大小搞错(512字节 vs 4096字节)。
  • 分区类型不匹配:Bootloader期望的分区类型与实际写入的不一致,导致无法识别。
  • 属性标志冲突:多个分区同时设置了启动标志,Bootloader不知道该启动哪个。
  • 备份GPT损坏:主GPT和备份GPT都不一致,系统无法恢复。

重要提醒:分区表是Bootloader的命根子。我建议你在开发阶段就加上分区表完整性检查,包括CRC校验、魔数检查、地址范围检查。别等到量产了才发现问题——那时候改一个字节都要走完整的变更流程,代价太大了。

好了,这一章的内容就到这里。分区表的基础知识,说白了就是一张地图加一套规则。你理解了MBR和GPT的区别,掌握了分区类型和属性的含义,后面调试Bootloader分区问题就会顺手很多。