9、动态分区与Logical Volume:super分区概念、lpmake与lpdump工具、动态分区映射表、在OTA中处理动态分区

各位同学,今天我们来聊聊动态分区。说实话,这个知识点在车载OTA里属于「你绕不过去」的那种。我最早接触动态分区是在做某个车机项目时,客户要求系统分区能灵活调整大小,传统静态分区根本玩不转。嗯,那会儿我花了整整一周才把整套逻辑吃透。今天我把这些经验掰开揉碎讲给你听。

9.1 为什么需要动态分区?

传统Android分区是静态的——system、vendor、product这些分区在编译时就把大小定死了。你想想看,如果某次OTA需要增大system分区,但隔壁vendor还有空闲空间,怎么办?静态分区下只能干瞪眼。

动态分区的核心思路很简单:把物理分区抽象成逻辑卷。说白了,就是用一个「超级分区」(super分区)来管理所有系统分区的物理空间,各个逻辑分区(logical volume)按需从super里分配空间。

核心概念:super分区是一个物理分区,里面包含了多个逻辑分区(如system_a、vendor_a、product_a)。逻辑分区的大小可以动态调整,只要super分区总空间够用就行。

9.2 super分区长什么样?

super分区本质上是一个容器。它的布局分为三部分:

  • 头部(Header):记录super分区的版本、总大小、逻辑分区数量等信息
  • 几何描述(Geometry):描述super分区的物理布局,包括槽位信息
  • 逻辑分区表(Logical Partition Table):记录每个逻辑分区的名称、大小、起始偏移等

我习惯把super分区比作一个「硬盘柜」——柜子本身有固定尺寸,但里面的抽屉(逻辑分区)可以自由调整大小。每个抽屉都有标签(分区名),比如system_a、vendor_b。

个人经验:super分区的大小通常在4GB到8GB之间,具体取决于你的系统镜像有多大。我在一个项目中遇到过super分区设小了,导致OTA时空间不足,最后不得不重新刷机。所以,super分区大小一定要留够余量,建议至少比所有逻辑分区总和多出20%。

9.3 lpmake:创建动态分区的瑞士军刀

lpmake是Android提供的工具,用来生成super分区的镜像。它的工作流程是这样的:

  1. 定义所有逻辑分区的名称、大小、属性
  2. 指定super分区的总大小和槽位数量
  3. lpmake把这些信息打包成一个完整的super.img

来看一个实际例子:

# 创建一个super分区镜像
lpmake \
  --metadata-size 65536 \
  --super-name super \
  --metadata-slots 2 \
  --device super:4294967296 \
  --group main_a:2147483648 \
  --group main_b:2147483648 \
  --partition system_a:readonly:1073741824:main_a \
  --partition vendor_a:readonly:536870912:main_a \
  --partition product_a:readonly:536870912:main_a \
  --partition system_b:readonly:1073741824:main_b \
  --partition vendor_b:readonly:536870912:main_b \
  --partition product_b:readonly:536870912:main_b \
  --sparse \
  --output super.img

这里我解释几个关键参数:

  • --metadata-slots 2:表示有两个槽位(A/B分区),每个槽位都有自己的元数据
  • --group:定义分区组,同一个组内的逻辑分区共享空间
  • --partition:定义具体的逻辑分区,格式是「名称:属性:大小:所属组」
  • --sparse:生成稀疏镜像,减少镜像文件大小

注意:lpmake生成的super.img是稀疏格式的,烧录到设备时需要先解稀疏。我曾经犯过一个低级错误——直接把稀疏镜像dd到super分区,结果设备起不来。嗯,后来我学乖了,每次都用simg2img先转换。

9.4 lpdump:查看动态分区的「CT扫描仪」

lpdump用来解析super分区里的元数据,把逻辑分区的布局信息dump出来。这个工具在调试时特别有用。

# 从super.img中读取元数据
lpdump --slot 0 super.img

# 从设备上直接读取
adb shell lpdump --slot 0 /dev/block/by-name/super

输出结果大概长这样:

Slot 0:
  Name: system_a
  Group: main_a
  Size: 1073741824 bytes (1024 MB)
  Extents:
    0: 0..2097151 (linear, 2097152 sectors)
  
  Name: vendor_a
  Group: main_a
  Size: 536870912 bytes (512 MB)
  Extents:
    0: 2097152..3145727 (linear, 1048576 sectors)
  
  Name: product_a
  Group: main_a
  Size: 536870912 bytes (512 MB)
  Extents:
    0: 3145728..4194303 (linear, 1048576 sectors)

看到那个Extents了吗?它表示逻辑分区在super分区里的物理位置。比如system_a从第0个扇区开始,占了2097152个扇区(每个扇区512字节)。

避坑指南:我曾经在调试时发现lpdump输出的分区大小和实际不符,后来发现是metadata-slots参数设错了。如果你看到分区大小异常,先检查metadata-slots是不是和实际槽位数一致。

9.5 动态分区映射表:内核怎么找到逻辑分区?

设备启动时,内核需要知道每个逻辑分区在super分区里的位置。这个映射关系是通过设备映射器(Device Mapper)实现的。

流程是这样的:

  1. Bootloader读取super分区的元数据
  2. 根据元数据创建dm-linear设备映射
  3. 每个逻辑分区对应一个dm-linear设备,比如system_a映射到/dev/dm-0
  4. 内核通过这些dm设备访问逻辑分区

用命令查看映射表:

# 查看所有dm设备
adb shell ls -la /dev/block/dm-*

# 查看具体映射关系
adb shell dmsetup table system_a

输出示例:

0 2097152 linear 259:0 0

这表示:从扇区0开始,共2097152个扇区,映射到主设备号259、次设备号0的设备(也就是super分区),偏移为0。

9.6 OTA中如何处理动态分区?

OTA升级时,动态分区的处理比静态分区复杂得多。核心问题有两个:

  • 空间分配:新版本的分区大小可能变了,需要重新分配空间
  • 数据迁移:如果分区位置变了,需要把数据搬到新位置

Android的OTA机制通过update_engine来处理这些。大致流程如下:

  1. 解析OTA包中的新super分区布局
  2. 检查当前super分区是否有足够空间
  3. 如果有,直接调整逻辑分区大小(通过lpresize工具)
  4. 如果没有,需要先合并或压缩现有分区
  5. 写入新数据到对应的逻辑分区

这里有个关键点:动态分区支持在线调整大小。也就是说,系统运行时也能改分区大小,不需要重启。但要注意,调整大小期间不能读写该分区。

实战经验:我在做某个OTA方案时,遇到一个坑——新版本的vendor分区比旧版本大了200MB,但super分区里没有连续空间了。解决方案是:先把product分区缩小200MB,腾出空间给vendor。这个操作需要保证product分区里的数据不丢失,嗯,当时我用了lpresize的--shrink选项,配合文件系统resize,折腾了两天才搞定。

9.7 动态分区操作的核心命令

总结一下常用的动态分区操作命令:

命令 用途 示例
lpmake 创建super分区镜像 lpmake --device super:4G --group main:2G --partition system:1G:main
lpdump 查看super分区元数据 lpdump --slot 0 super.img
lpresize 调整逻辑分区大小 lpresize --size 2G /dev/block/by-name/system_a
lpadd 添加逻辑分区 lpadd --partition test:1G /dev/block/by-name/super
lpremove 删除逻辑分区 lpremove test /dev/block/by-name/super

重要提醒:lpresize、lpadd、lpremove这些操作都有风险,操作前一定要备份数据。我见过有人直接在量产设备上执行lpresize,结果分区表损坏,设备变砖。所以,永远不要在未测试的环境下执行这些命令

9.8 动态分区架构图

下面这张图展示了动态分区的整体架构,从super分区到逻辑分区的映射关系一目了然:

动态分区架构图 super分区(物理分区) 元数据(Metadata) system_a 逻辑分区1 vendor_a 逻辑分区2 设备映射器(Device Mapper) dm-linear映射:将逻辑分区映射到块设备 内核视角 /dev/dm-0 (system_a) /dev/dm-1 (vendor_a) /dev/dm-2 (product_a)

从这张图可以清楚看到:super分区是物理载体,里面包含元数据和多个逻辑分区。设备映射器负责把逻辑分区映射成内核可见的dm块设备。这样,上层文件系统就能像访问普通分区一样访问逻辑分区了。

9.9 小结

动态分区是Android车载系统OTA的核心技术之一。说白了,它解决了传统静态分区「空间无法灵活调整」的痛点。你需要掌握:

  • super分区的结构和布局
  • lpmake和lpdump的使用方法
  • 设备映射器的工作原理
  • OTA中动态分区的处理流程

我个人觉得,动态分区最难的不是概念,而是实际调试。尤其是分区大小调整时,一不小心就会搞坏分区表。我的建议是:先在模拟器上反复练习lpmake和lpresize的操作,等完全熟练了再上真机。

好了,动态分区的内容就讲到这里。记住一句话:super分区是容器,逻辑分区是抽屉,设备映射器是钥匙。搞懂这三者的关系,动态分区你就掌握了八成。


公众号:蓝海资料掘金营,微信deep3321