3、动态分区与逻辑卷:super 分区、Logical Volume Manager (LVM) 在车载中的应用

说到车载存储,有个话题绕不开——分区管理。

早期 Android 用静态分区表,每个分区大小写死在代码里。改一次分区大小,要重新刷 whole image。这在手机上行得通,毕竟用户刷机频率低。但车载不一样,OTA 升级频繁,分区大小说变就变。静态分区?太死板了。

所以 Google 引入了动态分区,后来又有了 LVM。今天我们就聊聊这两样东西在车载里怎么用。

3.1 为什么车载需要动态分区?

我参与的第一个车载项目,用的还是传统静态分区。当时遇到一个头疼的问题:system 分区不够用了,vendor 分区却空着一大半。想调整?得改分区表、重刷整机、重新做 OTA 包。折腾一圈下来,一周时间就没了。

你想想看,车载系统要跑 5-10 年,中间要升级多少次?每次升级,分区大小需求都可能变。静态分区根本扛不住这种变化。

动态分区就是为了解决这个问题。它把物理分区抽象成逻辑分区,大小可以动态调整。说白了,就是给分区管理加了一层「虚拟化」。

核心思想: 物理存储空间被统一管理,逻辑分区按需分配。你不再需要关心「system 分区到底多大」,只需要告诉系统「system 需要多少空间」。

3.2 Super 分区:动态分区的基石

Android 10 开始引入 super 分区。它是个「容器分区」,里面装着 system、vendor、product 等子分区。

我记得第一次看到 super 分区时,心里想:这不就是把多个分区打包成一个吗?后来深入一看,发现没那么简单。

3.2.1 Super 分区的结构

Super 分区内部使用「逻辑块」管理空间。每个子分区对应一组逻辑块,这些逻辑块在物理上可以不连续。有点像文件系统的碎片化,但管理得更精细。

// 查看 super 分区布局
$ lpdump /dev/block/by-name/super

Slot 0:
  Name: system_a
  Size: 2048 MB
  Extents:
    - [0, 1024]  // 物理块范围
    - [2048, 1024]  // 不连续,但逻辑上连续

  Name: vendor_a
  Size: 512 MB
  Extents:
    - [1024, 512]

Slot 1:
  Name: system_b
  Size: 2048 MB
  // ... 类似结构

看到没?system_a 的物理块是分两段的。这在静态分区里根本不可能,但动态分区完全没问题。

3.2.2 车载场景下的 super 分区配置

车载系统通常有 A/B 分区,super 分区也要支持双槽。我建议这样配置:

分区 建议大小 说明
super 4-8 GB 根据系统复杂度调整,预留 20% 余量
system 1.5-3 GB Android 框架 + 核心服务
vendor 512 MB - 1 GB 硬件抽象层 + 驱动
product 512 MB - 1 GB OEM 定制内容
odm 256-512 MB 设备专属配置

我的经验: 车载项目里,vendor 分区经常不够用。因为车厂会塞很多私有 HAL 和服务进去。我一般会多给 vendor 留 30% 的余量,省得后期折腾。

3.3 LVM:更灵活的逻辑卷管理

Super 分区解决了「分区大小可调」的问题,但还不够灵活。比如你想把 userdata 的一部分空间挪给 system 用,super 分区就做不到了。

LVM(Logical Volume Manager)就是来补这个短板的。它把整个存储空间(包括 super、userdata、metadata 等)统一管理,形成一个「卷组」。然后从卷组里切出「逻辑卷」给各个分区用。

3.3.1 LVM 在车载中的典型架构

物理存储 (eMMC/UFS)
    |
    ├── 物理分区 1: bootloader
    ├── 物理分区 2: misc
    ├── 物理分区 3: super (动态分区)
    └── 物理分区 4: userdata (LVM 卷组)
            |
            ├── 逻辑卷: system_ext
            ├── 逻辑卷: vendor
            ├── 逻辑卷: product
            ├── 逻辑卷: odm
            └── 逻辑卷: cache (可选)

嗯,这里要注意:LVM 不是替代 super 分区,而是和它配合使用。Super 分区管「系统分区」,LVM 管「数据分区」。两者分工明确。

3.3.2 车载 LVM 的配置示例

我在一个项目中用过这样的配置:

// BoardConfig.mk 中的 LVM 配置
BOARD_SUPER_PARTITION_SIZE := 6442450944  // 6GB
BOARD_SUPER_PARTITION_GROUPS := group_a group_b

// 定义逻辑卷
BOARD_GROUP_GROUP_A_SIZE := 3221225472  // 3GB
BOARD_GROUP_GROUP_A_PARTITION_LIST := system_a vendor_a product_a

// LVM 卷组配置
BOARD_LVM_VOLUME_GROUP_SIZE := 8589934592  // 8GB
BOARD_LVM_LOGICAL_VOLUMES := \
    system_ext:2048M \
    vendor:1024M \
    product:1024M \
    odm:512M \
    cache:1024M

我曾经踩过的坑: LVM 的元数据会占用额外空间。我算分区大小时忘了算这 4MB 的元数据开销,结果 OTA 升级时空间差一点点,导致升级失败。后来我学乖了,每次算分区大小都多留 10MB 给元数据。

3.4 动态分区与 LVM 的协同工作

实际车载系统中,super 分区和 LVM 是配合使用的。我画了个简化的流程图:

系统启动流程:
1. Bootloader 加载 boot 分区
2. 内核启动,挂载 super 分区
3. init 进程解析 super 分区表,挂载 system/vendor/product
4. 挂载 userdata 分区,激活 LVM 卷组
5. 挂载 LVM 逻辑卷(system_ext、vendor 等)
6. 启动 Android 框架

这里有个关键点:super 分区在 bootloader 阶段就要可用,所以它必须是物理分区。而 LVM 卷组在内核启动后才激活,所以适合管理数据分区。

3.4.1 OTA 升级时的分区调整

动态分区最大的优势在 OTA 时体现。假设这次升级需要给 system 多 200MB 空间:

  1. OTA 包解析新分区表,发现 system 需要扩容
  2. 系统检查 super 分区是否有空闲空间
  3. 如果有,直接从空闲池里划 200MB 给 system
  4. 如果没有,从其他分区(比如 vendor)回收空间
  5. 更新分区表,重启后生效

整个过程不需要重新刷机,用户无感。这在车载场景下太重要了——你总不能要求车主把车开到 4S 店去升级吧?

我个人的习惯: 每次做 OTA 包时,我都会在 super 分区里留 10-15% 的空闲空间。这样即使升级过程中需要临时调整分区大小,也有缓冲余地。别把空间用得太满,给自己留条后路。

3.5 车载场景下的特殊考量

车载和手机不一样,有几个特殊点要注意:

  • 可靠性要求更高: 车规级存储芯片的寿命和稳定性要求更严。LVM 的元数据要定期备份,防止意外损坏。
  • 启动时间敏感: 车载系统要求快速启动(通常 < 10 秒)。动态分区和 LVM 的初始化不能太慢。我建议把 LVM 的激活放在 init 早期,并行执行。
  • 安全启动约束: Super 分区的内容要参与 dm-verity 校验。调整分区大小后,要重新计算哈希树。
  • 多用户场景: 车载可能有多个用户(驾驶员、乘客),每个用户的数据隔离要用 LVM 的快照功能。

说白了,动态分区和 LVM 给车载存储管理带来了极大的灵活性。但灵活性是有代价的——你需要更精细地管理空间,更谨慎地处理边界情况。

我在项目中遇到过一个问题:LVM 卷组里的逻辑卷太多(超过 10 个),导致内核挂载时超时。后来我把一些不常用的逻辑卷合并了,才解决这个问题。所以我的建议是:逻辑卷数量控制在 6-8 个以内,别贪多。

3.6 小结

动态分区和 LVM 是车载 Android 存储管理的核心。Super 分区解决了「系统分区动态调整」的问题,LVM 解决了「数据分区灵活管理」的问题。两者配合,才能应对车载系统 5-10 年的 OTA 升级需求。

下一章,我们会聊聊存储性能优化。车载系统的存储性能瓶颈在哪里?怎么用 F2FS 和 I/O 调度器来优化?到时候细说。