2、分区概念入门:什么是分区、为什么需要分区、分区表的基本结构(MBR与GPT)

说实话,我刚入行那会儿,对分区的理解也就停留在「C盘、D盘」这个层面。直到第一次在嵌入式板子上调 U-Boot,发现内核启动到一半就挂了,原因是分区表读错了。嗯,从那以后,我才真正开始认真对待分区这件事。

这一节,咱们就把分区这事儿彻底聊透。别觉得它基础,很多底层问题追根溯源,都是分区没搞对。

2.1 什么是分区?

分区,说白了就是把一块物理存储设备(比如 eMMC、NAND Flash、SD 卡)逻辑上切成几块。每一块可以独立使用,互不干扰。

举个例子:你有一块 8GB 的 eMMC。你可以把它分成:

  • 2GB 给 bootloader(U-Boot 自己住的地方)
  • 1GB 给内核和设备树
  • 4GB 给根文件系统
  • 1GB 给用户数据

每个分区都有自己的起始地址和大小。操作系统和 bootloader 通过分区表来知道这些信息。

核心要点:分区是逻辑概念,不是物理切割。它只是告诉系统「从哪到哪属于谁」。

2.2 为什么需要分区?

你可能会问:我直接把整个 Flash 当一块用不行吗?行,但你会遇到一堆麻烦。

我个人习惯把分区的好处总结为四点:

  1. 隔离风险:文件系统崩了,不会把 bootloader 也带崩。我在项目里见过有人把内核和根文件系统放同一个分区,结果一次意外断电,两个都坏了,板子直接变砖。
  2. 方便升级:OTA 升级时,只更新系统分区,不动 bootloader 和数据分区。这样就算升级失败,还能回滚。
  3. 满足不同需求:不同分区可以用不同的文件系统。比如 bootloader 分区用 raw 格式,根文件系统用 ext4,数据分区用 FAT32(方便 Windows 读取)。
  4. 启动流程要求:U-Boot 启动时,需要从特定位置加载内核。没有分区表,它根本不知道去哪找。

我的经验:在嵌入式设备上,我建议至少分 4 个区:bootloader、kernel、rootfs、data。再多一个 recovery 分区做备份,会更安全。

2.3 分区表的基本结构

分区表就是一张「地图」,记录了每个分区的起始位置、大小、类型等信息。目前主流的分区表格式有两种:MBR 和 GPT。

2.3.1 MBR(Master Boot Record)

MBR 是传统分区表格式,诞生于 1983 年。它长这样:

  • 位于设备的第一个扇区(LBA 0)
  • 大小 512 字节
  • 前 446 字节是引导代码
  • 接着 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 位表示,最多 2^32 个扇区,每个扇区 512 字节,算下来就是 2TB。超过这个容量,必须用 GPT。

我曾经在一个项目里用 MBR 给 4TB 的 SSD 分区,结果系统只认出了 2TB。查了半天才发现是 MBR 的锅。后来换成 GPT,问题就解决了。

2.3.2 GPT(GUID Partition Table)

GPT 是 MBR 的替代者,属于 UEFI 规范的一部分。它解决了 MBR 的很多痛点:

  • 支持 2TB 以上的磁盘(最大 8ZB)
  • 支持最多 128 个分区(Windows 限制,理论上更多)
  • 每个分区有唯一的 GUID,不会冲突
  • 有备份分区表,容错性更好

GPT 的结构是这样的:

LBA 0: 保护 MBR(防止老工具误识别)
LBA 1: GPT 头部(主)
LBA 2~33: 分区条目(主,每个条目 128 字节)
LBA 34~: 数据区
...
最后 33 个 LBA: 分区条目(备份)
最后 1 个 LBA: GPT 头部(备份)

每个 GPT 分区条目包含:

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

建议:在 U-Boot 下做分区适配,我推荐用 GPT。原因很简单:灵活、容错好、支持大容量。而且 U-Boot 对 GPT 的支持已经很成熟了。

2.4 MBR vs GPT:怎么选?

我整理了一个对比表,方便你快速决策:

对比项 MBR GPT
最大容量 2TB 8ZB
最大分区数 4 个主分区(或 3 主 + 1 扩展) 128 个(默认)
备份分区表 无 有(头部和条目都有备份)
分区标识 1 字节类型码 128 位 GUID
兼容性 所有系统都支持 需要 UEFI 或较新系统
U-Boot 支持 完全支持 完全支持(需开启 CONFIG_PARTITION_UUIDS)

你想想看,如果你的设备容量小于 2TB,而且不需要太多分区,MBR 完全够用。但如果你在做新项目,我建议直接上 GPT。省得以后扩容时还要折腾。

2.5 分区在 U-Boot 中的体现

在 U-Boot 里,分区信息是通过 part 命令来查看和操作的。比如:

U-Boot> part list mmc 0

Partition Map for MMC device 0  --  使用 GPT

Part    Start LBA       End LBA         Name
   1     0x00000800      0x00002000      "bootloader"
   2     0x00002000      0x00004000      "kernel"
   3     0x00004000      0x00010000      "rootfs"
   4     0x00010000      0x00020000      "data"

U-Boot 启动时,会根据分区表找到内核和设备树的位置。比如:

# 从 mmc 0 的 kernel 分区加载内核
load mmc 0:2 ${kernel_addr_r} /boot/zImage

这里的 0:2 表示「设备 0 的第 2 个分区」。U-Boot 会自动解析分区表,找到对应的 LBA 地址。

避坑:我曾经遇到过一个问题:分区表写对了,但 U-Boot 就是找不到内核。后来发现是分区起始地址没对齐到 1MB 边界。很多 eMMC 要求分区起始地址是 1MB 的整数倍,否则性能会下降,甚至无法启动。记住这个教训:分区起始地址一定要对齐。

2.6 一张图看懂分区体系

下面这张图展示了从物理设备到分区表,再到 U-Boot 使用的完整链路:

分区体系与 U-Boot 适配链路 物理存储设备 eMMC / NAND Flash / SD 卡 / SSD 分区表 MBR(传统) / GPT(现代) 记录每个分区的起始 LBA、大小、类型、名称 MBR 结构 引导代码(446B) + 分区表(64B) + 魔数(2B) 最多 4 个主分区,最大 2TB GPT 结构 保护 MBR + GPT 头部 + 分区条目 最多 128 分区,支持 2TB+ U-Boot 解析分区表 → 加载内核/设备树 汇聚到 U-Boot

这张图把整个链路串起来了。从物理设备开始,经过分区表(MBR 或 GPT),最终由 U-Boot 解析并加载系统组件。你在做分区适配时,本质上就是在操作中间那层——分区表。

2.7 小结

分区这事儿,说难不难,说简单也不简单。关键是要理解它的本质:分区表就是一张地图,告诉系统「东西在哪」。

我个人建议,在嵌入式项目里优先用 GPT。虽然 MBR 也能用,但 GPT 的灵活性和容错性更好。尤其是当你需要动态调整分区大小时,GPT 的优势就体现出来了。

嗯,这一节就到这里。记住:分区表搞对了,后面的事情就顺了。搞不对,你会在各种奇怪的地方踩坑。