这篇文章的内容,是学习"按工业标准思路,做 eMMC 分区设计"
目的是:学习工业嵌入式 Linux 存储架构的方案。
本文采用的手动 fdisk 分区 ,适合学习。工业量产:必须自动化 + 固化镜像。
下一篇博客内容,实现了 sfdisk 脚本,一键 分区、格式化。
存储架构:
|-----------------------|-------------------------------|---------------------|
| 设计项 | 工业意义 | 行业应用 |
| A/B 系统 | 支持 OTA 升级、无缝切换、失败回滚 | 手机 / IoT / 车载 / 工控 |
| Recovery 独立分区 | 系统修复、刷机、救援模式,不依赖主系统 | 工控 / 车载 / 手机 |
| Data 分离 | 用户数据与系统隔离,系统升级不影响数据 | 手机 / 工业设备 / 嵌入式系统 |
| Bootloader / env 独立分区 | 启动链与系统隔离,保证启动可靠性 | 所有嵌入式 Linux 设备 |
| Env 独立分区 | U-Boot 环境变量持久化存储,防止文件系统损坏影响启动 | 工业控制器 / 路由器 / SoC设备 |
| 预留空间(p9 / tail / OP) | 用于对齐、坏块替换、未来扩展或 eMMC 内部磨损均衡 | 高可靠工业设备 / 长生命周期产品 |
OTA : Over-The-Air 远程无线升级。
下图是 开发板 emmc 分区计划:
|--------|---------|----------|----------|------------|----------------------------------------------|
| 分区 | 大小 | 起始扇区 | 结束扇区 | 文件系统 | 工业用途说明 |
| p1 | 4MB | 2048 | 10239 | raw / nofs | U-Boot 备份 |
| p2 | 1MB | 10240 | 12287 | raw / env | U-Boot 环境变量存储区 |
| p3 | 64MB | 12288 | 143359 | FAT32 | Boot A 分区(kernelA + dtbA ) |
| p4 | 64MB | 143360 | 274431 | FAT32 | Boot B 分区(kernelB + dtbB ) |
| p5 | 2GB | 274432 | 4468735 | ext4 | rootfs A |
| p6 | 2GB | 4468736 | 8663039 | ext4 | rootfs B / OTA备份 |
| p7 | ~3.4GB | 8663040 | 14901247 | ext4 | Data(用户数据 / 应用数据 / 日志 / OTA缓存 + 预留空间) |
| p8 | 128MB | 14901248 | 15114239 | ext4 | Recovery / Rescue System(出厂前烧录最小救援系统 / 刷机环境) |
下图是 每个分区的类型:
83 = Linux 原生分区 (对应 ext4 / ext3 / ext2 等)
c = W95 FAT32 (LBA) (对应 FAT32 文件系统)

A/B 独立分区: bootA,bootB ,rootfs A ,rootfs B,每个都是独立文件系统。
p8 分区的后面,还剩约 76MB 空间,没分完,留作 eMMC 磨损均衡备用。
数据分离的逻辑,设置各个分区的读写权限和用户的权限:
-
系统分区(rootfsA、rootfsB):日常运行全程设为只读(ro),仅在OTA升级更新系统镜像时,临时重新挂载为可读写,升级结束立刻切回只读,避免人为误改、恶意篡改导致系统损坏;
-
启动分区(放内核、设备树、U-Boot环境变量的分区):保持只读属性,防止启动关键文件被误删,保障开机稳定性;
-
Data 用户分区:全程保持可读可写,用户所有自建文件、业务数据、自定义程序、动态日志都只能存放在这里。
-
限制普通用户的权限。
关于分区表:
imx6ull开发板的 U-Boot 版本是 2016.03 ,配置里没有 GPT 支持,分区表 支持 MBR ,属于2016年前后的技术栈。
MBR: 简单,兼容性好,但脆弱,4 主分区限制。
GPT: 健壮,冗余备份,但 i.MX6ULL ROM 不支持。
GPT 标准支持 128 个分区。
MBR 格式限制:最多只能有 4 个主分区,超过必须用扩展分区。
方案A:p4 扩展分区 + 逻辑分区(p5~p9),独立块设备,隔离性好。
方案B:单分区 + 多个子目录替代 p5~p9,隔离级别降低。
方案C:升级 U-Boot 至 2022+,用户区切 GPT,直接分 8 个独立分区。(ROM 仍需 boot0 启动 U-Boot,
imx6ULL 的 Boot ROM 不支持 GPT,但 U-Boot 本身和 Linux 内核均支持 GPT)。
升级 U-Boot 是独立工程,脱离正点原子/NXP 的 BSP 支持后,DDR 时序、设备树、板级文件全部需要自己重写适配。
解决方案:选择方案A,p1~p3 占 3 个主分区槽位,p4 改成扩展分区(里面装的是逻辑分区的链表),承载 p5~p9。学习开发阶段可用,先用 U-Boot 2016.03 把系统全部跑通。



现在开始实操:
第一步, 添加格式化工具 :
因为是 sd卡启动,要确保 运行的linux 系统,有格式化工具。
进入 Buildroot 的配置界面, 添加格式化工具,到根文件系统里。
如果不做OTA 远程升级,可以不安装后面的 OTA 工具 。
- dosfstools(提供 mkfs.vfat)
Target packages → Filesystem and flash utilities → \* dosfstools , \* mkfs.vfat
- e2fsprogs(提供 mkfs.ext4/ext3/ext2)
Target packages → Filesystem and flash utilities → \* e2fsprogs

( 不需要 额外勾选子选项 )

- mmc-utils ( mmc 工具 )
Target packages → Filesystem and flash utilities -> Filesystem and flash utilities -> mmc-utils

- sfdisk (自动化 分区脚本需要的工具 )
Target packages → System tools -> util-linux -> basic set
如果编译后,显示没有sfdisk分区工具,buildroot 根目录 执行 make util-linux-dirclean ,再重新编译。

- fw_printenv ( OTA 逻辑的核心工具 )
可以在 Linux 下安全读写 U-Boot 环境变量。
A/B 系统的核心是无缝切换和防变砖,完全依赖 fw_setenv
Target packages → Filesystem and flash utilities -> Hardware handling -> fw_printenv

6 . wipefs 和 losetup ( OTA 需要的工具 )
Target packages → System tools -> util-linux ->


重新编译 Buildroot ,解压, 我是nfs挂载的根文件系统。
第二步, emmc 擦掉旧分区表 :
sd卡启动, 运行linux 系统
cd /dev/
ls mmc*
fdisk -l mmcblk1

没有分区表,可以直接开始分区

如果有分区表,擦掉旧分区表:

执行命令: dd if=zero of=mmcblk1 bs=512 count=1
命令解释:把 eMMC 的第 0 扇区(前 512 字节)全部写零。
这 512 字节里存的是:
MBR 分区表(446 字节的引导代码 + 64 字节的 4 个主分区条目)
磁盘签名(2 字节 0x55AA)
擦掉后 eMMC 就变回"没有分区表"的状态,相当于一张白纸,可以重新分区。
第三步,开始分区:
1. 执行分区命令: fdisk mmcblk1
按顺序敲,示例: n 回车, p回车
(如果参数输入错了,q 退出 ,重新分区):

下面是 p4 ( 扩展分区 ) 的示例图:
从扇区 143360 到 默认扇区 15269887 ,也就是从 0.068G 到 7.28G 之间

2. 创建逻辑分区: 按顺序继续敲
Command: n
First sector: 直接回车
Last sector: +64M
Command: n
First sector : 直接回车
Last sector: +2G
Command: n
First sector: 直接回车
Last sector: +2G
Command: n
First sector : 直接回车
Last sector: +3G
Command: n
First sector: 直接回车
Last sector: 回车(吃满剩余)
3. 1、3、5分区 设置为 FAT32 类型: 按顺序继续敲

检查并写入,输入 p 和 w:
Command: p ← 先看一眼对不对
Command: w ← 确认无误再写

基本上全分完了,末尾没有多余空间。p9 实际 154M 左右,和前面预估略有偏差,但完全没问题。
验证:
fdisk -l mmcblk1

第四步,格式化:
格式化 p3、p5、p6、p7、p8、p9。不格式化 p1、p2、p4 。
mkfs.fat /dev/mmcblk1p3
mkfs.fat /dev/mmcblk1p5
mkfs.ext4 /dev/mmcblk1p6
mkfs.ext4 /dev/mmcblk1p7
mkfs.ext4 /dev/mmcblk1p8
mkfs.ext4 /dev/mmcblk1p9
全部格式化完后,验证:
fdisk -l mmcblk1

挂载测试(测试完后,卸载所有挂载点):
挂载测试是为了验证文件系统没有损坏,能正常读写。
mkdir -p /mnt/bootA /mnt/bootB /mnt/rootA /mnt/rootB /mnt/data /mnt/recovery
mount /dev/mmcblk1p3 /mnt/bootA
mount /dev/mmcblk1p5 /mnt/bootB
mount /dev/mmcblk1p6 /mnt/rootA
mount /dev/mmcblk1p7 /mnt/rootB
mount /dev/mmcblk1p8 /mnt/data
mount /dev/mmcblk1p9 /mnt/recovery
df -h


emmc 烧系统时,参考上面这个表格。
卸载所有挂载点:
umount /mnt/bootA
umount /mnt/bootB
umount /mnt/rootA
umount /mnt/rootB
umount /mnt/data
umount /mnt/recovery
验证已卸载,确认不再有 /mnt/bootA 等条目。
df -h

清理挂载点目录(可选)
rmdir /mnt/bootA /mnt/bootB /mnt/rootA /mnt/rootB /mnt/data /mnt/recovery
第五步,烧系统:
eMMC 分区工作全部完成,可以开始烧系统了。
我是nfs挂载的根文件系统 ,通过 NFS 传到板子上烧录。
1. 在 Ubuntu 主机上,把 u-boot.imx 内核、设备树、根文件系统压缩包 拷贝到 NFS 上自己创建的目录里


2. 在板子上,烧录 U-Boot 到 eMMC boot0 区
解除 boot0 写保护
echo 0 > /sys/block/mmcblk1boot0/force_ro
烧录 U-Boot
dd if=u-boot.imx of=/dev/mmcblk1boot0 bs=512 seek=2
恢复 boot0 写保护
echo 1 > /sys/block/mmcblk1boot0/force_ro

3. 设置 eMMC 从 boot0 启动
mmc bootpart enable 1 1 /dev/mmcblk1
然后验证
mmc extcsd read /dev/mmcblk1 | grep PARTITION_CONFIG
显示 PARTITION_CONFIG: 0x48 就对了

**4.**拷贝内核和 dtb 到 bootA(分区3)
mount /dev/mmcblk1p3 /mnt
cp zImage /mnt/
cp imx6ull-alientek-emmc.dtb /mnt/
然后验证
ls /mnt
umount /mnt

5. 解压根文件系统到 rootfs A(分区6)
mount /dev/mmcblk1p6 /mnt
tar -xaf rootfs.tar -C /mnt
然后验证
ls /mnt
umount /mnt

第五步,开机测试:
开发板的拨码开关,选择 emmc 启动,开机

U-Boot 从 eMMC 成功启动了
在 u-boot 命令行,查看分区信息
分区表 正确,U-Boot 能识别所有 9 个分区,p4 是扩展分区

查看 p3(bootA)里的文件
fatls mmc 1:3

如果有 zImage 和 dtb,直接启动
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p6 rootwait'
fatload mmc 1:3 0x80800000 zImage
fatload mmc 1:3 0x83000000 imx6ull-alientek-emmc.dtb
bootz 0x80800000 - 0x83000000

登录账号后,验证系统:
df -h
cat /proc/partitions | grep mmcblk1


完美,全部正确。
现在做两件事收尾:
1. 固化 U-Boot 环境变量:
重启开发板,设置环境变量,
设置:从分区3 (bootA) 加载内核 设备树
setenv bootcmd 'fatload mmc 1:3 0x80800000 zImage; fatload mmc 1:3 0x83000000 imx6ull-alientek-emmc.dtb; bootz 0x80800000 - 0x83000000'
设置:开机使用分区6 (rootfs A) 的根文件系统
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p6 rootwait'
保存环境变量 saveenv
启动内核 boot
2. 把 bootA(分区3) 里的内核、设备树 拷贝到 bootB(分区5),目的是先备份,升级时再替换。
先挂 bootA,再挂 bootB,把文件拷过去:
mkdir -p /mnt/bootA /mnt/bootB
mount /dev/mmcblk1p3 /mnt/bootA
mount /dev/mmcblk1p5 /mnt/bootB
cp /mnt/bootA/* /mnt/bootB/
ls /mnt/bootB
umount /mnt/bootA
umount /mnt/bootB

现在 p8 (数据分区) 是空的,没挂载,所以看不到。
查看当前挂载 df -h

手动挂载数据区
mkdir -p /data
mount /dev/mmcblk1p8 /data
df -h
取消挂载 umount /data

如果想开机自动挂载
mkdir -p /data
mount /dev/mmcblk1p8 /data
echo "/dev/mmcblk1p8 /data ext4 defaults 0 0" >> /etc/fstab
cat /etc/fstab

设置 开机自动挂载 后,重启开发板:
进入 data 目录

|------------------|------------------------------|
| 目录 | 用途 |
| /data | 数据区,随便读写 |
| /data/lost+found | ext4 文件系统修复后找回的损坏文件碎片,不要手动动它 |
以后,你的的应用数据、日志、用户文件全部放 /data 目录下,不是 /data/lost+found
lost+found 是每个 ext4 分区格式化后自动生成的系统目录,正常使用不用管。
home 目录 和 data 目录 的 区别:
|-------|--------------|---------------------|
| 目录 | 位置 | 用途 |
| /home | p6(rootfs A) | 根文件系统,系统的一部分,升级会被覆盖 |
| /data | p8(data) | 独立分区,升级不丢 |
当前状态总结:
|---------------|---------------|------------------------------|
| 分区 | 内容 | 状态 |
| p1 bootloader | --- | 4M 预留 |
| p2 env | U-Boot 环境变量备份 | 空的,需要重新编译 U-Boot,指定 env 存 p2 |
| p3 bootA | zImage + dtb | 已就绪 |
| p4 扩展 | --- | |
| p5 bootB | zImage + dtb | 内核、设备树的备份,已就绪 |
| p6 rootfs A | rootfs | 已挂载运行 |
| p7 rootfs B | 空 | 待 OTA 时写入(远程升级) |
| p8 data | 空 | 已挂载,应用层使用 |
| p9 recovery | 空 | 待装救援系统 |
p1 和 p2 分区是空的,暂时没用上。
boot1 分区 其实也可以做U-Boot备份,boot0 + boot1 双备份数据,可以覆盖 U-Boot 冗余需求,p1 分区就显得有点多余了。把p1这个主分区槽位 腾出来给 rootA 或 recovery,重新分区,比留着 p1 空着 更好。暂时先这样,先跑通了再说,当作学习了解,有时间再改。
p9 recovery 分区:出厂前 预装最小救援系统,A/B 双系统如果都挂了,最后用它救援。
p9 Recovery 分区内容清单:见下图

关于升级:
正常启动 → rootfs A 根文件系统
OTA 升级 → 新系统写到 rootfs B → 切换启动 → rootfs B
下次升级 → 新系统写到 rootfs A → 切换启动 → rootfs A

工业级 OTA 的标准实现方式:
-
设备正常运行在 A 槽
-
服务器推送新版本
-
下载新内核到 p5,新 rootfs 到 p7
-
U-Boot 切启动到 B 槽
-
重启 → 运行新版本
-
如果新版本连续启动失败 → U-Boot 自动切回 A
到这里, emmc 的分区,烧系统,测试,结束了。
分区、烧系统 :也可以用 sfdisk 分区工具 做成自动化脚本,取代手动输入。
eMMC 分区设计,也可以根据自己的需要,做优化。
MFG Tool 烧录配置文件:

imx6ull 学习资料里的这个 xml 文件,是 NXP 官方的 MFG Tool(Manufacturing Tool)烧录配置文件。
两种烧录模式:
|-----------|-----------------------|
| 模式 | 说明 |
| "Install" | 擦除介质 + 烧录完整固件(新板首次烧录) |
| "Update" | 只更新固件,不擦除用户数据(OTA 场景) |
xml 文档 和 本文,做对比:
|-----------|------------------------------|-----------------------------------------|
| 步骤 | NXP 官方 | 本文的做法 |
| 烧录工具 | MFG Tool(USB OTG + 临时 Linux) | 从 SD 卡/NFS 启动 Linux,手动 dd/tar |
| 分区 | 调用 mksdcard.sh 脚本 | 手动 fdisk,一步步敲 |
| U-Boot 位置 | eMMC boot0,seek=2 | 相同 |
| 启动设置 | mmc bootpart enable 1 1 | 相同 |
| 内核/dtb | 放 p1(FAT32) | 放 p3 bootA、p5 bootB |
| rootfs | 放 p2(ext3) | 放 p6 rootfs A , p7 rootfs B ,升级时切换根文件系统 |
| 分区方案 | 简单 2 分区(p1 boot, p2 rootfs) | 9 分区 A/B 工业方案 |
关键点
- NXP 的分区方案非常简单(2 个分区),没有 A/B、没有 recovery、没有 data 隔离
- 烧录流程依赖 USB OTG 进入 BootStrap 模式,不需要 SD 卡
- Android/Brillo 的方案里才有 A/B 分区(boot_a/boot_b、system_a/system_b)