分区表基础:存储布局的“地图”

大家好,我是你们的烧录工具课讲师。今天咱们聊聊分区表。说实话,很多工程师干了几年,对分区表还是一知半解。我见过有人把分区表搞错,导致整批设备变砖,那场面……嗯,咱们今天就把这事彻底讲透。

分区表是什么?说白了,它就是一张“地图”。这张地图告诉烧录工具和Bootloader:你的Flash芯片里,哪块区域放Bootloader,哪块放系统镜像,哪块放用户数据。没有这张地图,烧录工具就是个瞎子。

核心定义:分区表是存储在存储设备起始位置的元数据,它定义了每个分区的起始地址、大小、属性和用途。

分区表的核心作用

我习惯把分区表的作用归纳为三点。你想想看,没有分区表会怎样?

  • 存储布局:告诉系统“哪里该放什么”。比如我的项目里,0x0000-0x8000放Bootloader,0x8000-0x100000放内核。这些地址全靠分区表来约定。
  • 权限管理:有些分区是只读的,比如系统分区;有些是可读写的,比如数据分区。分区表里会标记这些属性。我在做安全启动时,就靠这个防止用户误改系统区。
  • 升级策略:OTA升级时,系统需要知道哪个分区是当前版本,哪个是备份分区。分区表里通常会预留A/B分区,这就是升级的“双保险”。

我的经验:有一次客户反馈设备升级后无法启动。查了半天,发现是分区表里A/B分区的地址写反了。升级脚本把新镜像写到了旧分区,一重启就崩。从那以后,我每次都会在烧录前先校验分区表内容。

常见分区表格式

市面上主流的格式有三种:GPT、MBR,还有我们嵌入式工程师最熟悉的——自定义格式。我一个个说。

1. MBR(主引导记录)

MBR是上世纪80年代的老古董了。它放在存储设备的第一个扇区(512字节),里面包含引导代码和分区表。但它的限制很明显:

  • 最多支持4个主分区
  • 最大只能管理2TB的磁盘
  • 分区表只有64字节,每个分区16字节

我记得在2010年左右,有个项目用32GB的Flash,MBR完全够用。但现在动不动就128GB、256GB,MBR就力不从心了。

2. GPT(GUID分区表)

GPT是MBR的接班人。它使用全局唯一标识符(GUID)来标识分区,支持128个分区,最大管理9.4ZB的存储空间。而且它还在磁盘末尾备份了一份分区表,安全性高得多。

特性 MBR GPT
最大分区数 4个主分区 128个
最大磁盘容量 2TB 9.4ZB
备份分区表 无 有(磁盘末尾)
校验机制 无 CRC32校验
适用场景 老旧系统、小容量设备 PC、服务器、大容量存储

注意:GPT虽然强大,但Bootloader必须支持UEFI才能识别。如果你的MCU还是老式的Legacy Boot模式,那GPT可能不兼容。我曾经在一个ARM Cortex-M项目里踩过这个坑。

3. 自定义分区表

这才是咱们嵌入式工程师的“主战场”。为什么?因为很多MCU和SoC的Bootloader根本不认MBR或GPT。它们需要一种更轻量、更灵活的方式。

我参与过一个智能家居项目,用的是某国产MCU。它的Flash只有2MB,但需要分6个区:Bootloader、参数区、固件A、固件B、日志区、用户数据区。MBR只支持4个主分区,GPT又太重。怎么办?自己定义!

一个典型的自定义分区表结构长这样:

// 自定义分区表条目
typedef struct {
    uint32_t magic;          // 魔数,用于校验
    uint32_t offset;         // 分区起始地址
    uint32_t size;           // 分区大小
    uint32_t flags;          // 属性标志(只读、可升级、加密等)
    char     name[16];       // 分区名称
    uint32_t crc32;          // 本条目的CRC校验
} partition_entry_t;

// 分区表整体结构
typedef struct {
    uint32_t magic;          // 表魔数
    uint32_t entry_count;    // 条目数量
    uint32_t version;        // 分区表版本
    partition_entry_t entries[16]; // 最多16个分区
    uint32_t table_crc32;    // 整个表的CRC
} partition_table_t;

你看,这个结构只有几十个字节,但足够灵活。魔数用来识别是不是合法的分区表,CRC用来防篡改,flags字段可以定义各种属性。

避坑指南:我曾经在自定义分区表里忘了加版本号。结果有一次升级分区表后,旧Bootloader不识别新格式,直接死循环。从那以后,我每个分区表都带版本字段,并且Bootloader会做向前兼容。

分区表在烧录工具中的角色

烧录工具拿到一个镜像文件,第一步就是解析分区表。它需要知道:

  1. 分区表本身放在哪个地址(通常是Flash的0地址或固定偏移)
  2. 每个分区的起始地址和大小
  3. 哪些分区需要烧录,哪些保留
  4. 烧录前是否需要擦除整个分区

我习惯在烧录工具里加一个“分区表预览”功能。烧录前先读出设备里的分区表,和镜像里的分区表做对比。如果发现分区大小不匹配或地址冲突,直接报错,避免烧到一半才发现问题。

关键点:分区表是烧录流程的“第一道关卡”。它决定了后续所有数据写入的正确性。我建议在烧录工具的初始化阶段,优先校验分区表的完整性和合法性。

一张图看懂分区表

下面我用一张SVG图来展示分区表在Flash中的布局。这张图是我自己画的,你一看就明白。

分区表在Flash中的布局 Flash存储布局与分区表 Flash芯片(例如:16MB) 分区表(Partition Table) Bootloader 0x0000 - 0x8000 固件A(Active) 0x8000 - 0x80000 固件B(Backup) 0x80000 - 0xF8000 参数区(Config) 0xF8000 - 0x100000 用户数据区(User Data) 0x100000 - 0x1000000 分区表位于Flash起始位置,记录每个分区的地址、大小和属性

这张图展示了一个典型的嵌入式设备Flash布局。分区表在最前面,它像一本“目录”,告诉系统每个分区在哪里、有多大。注意看,固件A和固件B是A/B分区结构,这是OTA升级的基础。

小结

分区表这东西,说简单也简单,说复杂也复杂。但只要你理解了它的本质——一张描述存储布局的“地图”——剩下的就是细节了。我个人建议,在实际项目中,优先考虑使用自定义分区表,因为它最灵活、最可控。MBR和GPT在PC领域是标准,但在嵌入式领域,很多时候反而是累赘。

嗯,今天的内容就到这里。记住,分区表是烧录工具的第一步,也是最重要的一步。搞懂了它,后面的镜像管理、升级策略才能顺利展开。