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 当一块用不行吗?行,但你会遇到一堆麻烦。
我个人习惯把分区的好处总结为四点:
- 隔离风险:文件系统崩了,不会把 bootloader 也带崩。我在项目里见过有人把内核和根文件系统放同一个分区,结果一次意外断电,两个都坏了,板子直接变砖。
- 方便升级:OTA 升级时,只更新系统分区,不动 bootloader 和数据分区。这样就算升级失败,还能回滚。
- 满足不同需求:不同分区可以用不同的文件系统。比如 bootloader 分区用 raw 格式,根文件系统用 ext4,数据分区用 FAT32(方便 Windows 读取)。
- 启动流程要求: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 使用的完整链路:
这张图把整个链路串起来了。从物理设备开始,经过分区表(MBR 或 GPT),最终由 U-Boot 解析并加载系统组件。你在做分区适配时,本质上就是在操作中间那层——分区表。
2.7 小结
分区这事儿,说难不难,说简单也不简单。关键是要理解它的本质:分区表就是一张地图,告诉系统「东西在哪」。
我个人建议,在嵌入式项目里优先用 GPT。虽然 MBR 也能用,但 GPT 的灵活性和容错性更好。尤其是当你需要动态调整分区大小时,GPT 的优势就体现出来了。
嗯,这一节就到这里。记住:分区表搞对了,后面的事情就顺了。搞不对,你会在各种奇怪的地方踩坑。