20、OTA升级与VABC:Virtual AB Compress,压缩快照技术
聊到OTA升级,大家最熟悉的可能是传统的A/B分区方案。但今天我要讲的,是Google在Android 11里引入的一个“狠角色”——Virtual AB Compress,简称VABC。说白了,它就是在虚拟A/B的基础上,加了一层压缩快照技术。
我最早接触VABC是在一个存储空间吃紧的项目上。当时设备只有64GB,传统A/B分区要预留两套系统,用户能用的空间少得可怜。VABC的出现,算是把这个问题给解了。
为什么需要VABC?
先回顾一下痛点。传统A/B分区,你得有两套system、vendor、product。一套在用的叫slot A,另一套待命的叫slot B。升级时往B写,写完了切过去。好处是升级失败还能回滚,坏处是——空间浪费太严重。
虚拟A/B(Virtual AB)稍微好点。它不保留完整的slot B,而是用COW(写时复制)快照来记录差异。但问题来了:COW快照在升级过程中会越来越大,如果升级包很大,COW可能膨胀到跟完整分区差不多。我见过一个项目,OTA包2GB,COW最后涨到了3.5GB,差点把用户数据分区撑爆。
VABC就是来解决这个问题的。它的核心思路是:把快照数据压缩存储。你想想看,升级过程中写入的差异数据,很多都是重复的或者可以压缩的。压缩之后,COW占用的空间能减少50%到70%。
核心结论:VABC = 虚拟A/B + 压缩快照。它用CPU时间换存储空间,适合存储紧张的设备。
VABC的工作原理
VABC的工作流程,我习惯把它拆成三个阶段:
- 快照创建阶段:系统在升级开始时,为每个目标分区创建一个压缩快照设备(snapshot device)。这个设备背后是一个压缩的COW文件。
- 数据写入阶段:OTA服务把新数据写入快照设备。写入时,数据先被压缩,然后存储到COW文件中。注意,这里用的是在线压缩,边写边压。
- 快照合并阶段:升级完成后,系统重启。在第一次启动时,内核把压缩快照合并到目标分区。合并过程中,数据被解压并写入最终位置。
嗯,这里要注意:合并阶段是在重启后进行的。如果合并过程中断电或者崩溃,系统会回滚到升级前的状态。这就是VABC的“原子性”保证。
压缩算法与性能权衡
VABC默认使用lz4压缩算法。为什么选lz4?因为它快。OTA升级对延迟很敏感,你不能让用户等太久。lz4的压缩速度是gzip的3-5倍,解压速度更是快到飞起。
我曾经在项目上测试过不同算法的表现:
| 算法 | 压缩比 | 压缩速度 | 解压速度 | 适用场景 |
|---|---|---|---|---|
| lz4 | 2:1 ~ 3:1 | 极快 | 极快 | 默认推荐 |
| gzip | 3:1 ~ 5:1 | 慢 | 中等 | 存储极度紧张 |
| zstd | 3:1 ~ 6:1 | 中等 | 快 | 需要高压缩比 |
我个人习惯用lz4。除非你的设备存储真的捉襟见肘,否则没必要为了多压10%的空间去牺牲升级速度。你想想看,用户正在用手机,突然OTA下载完了要重启,结果合并花了5分钟——这体验能好吗?
VABC的架构图
下面这张图展示了VABC的核心架构。我画的是简化版,但关键组件都在了。
从图里可以看到,VABC涉及用户空间的OTA服务和快照管理,以及内核的dm-snapshot和dm-user。压缩COW文件存储在/data分区下。这个设计有个好处:压缩快照不占用额外的物理分区,全部挤在用户数据区里。
关键代码片段
VABC的配置在BoardConfig.mk里。我贴一段实际项目中的配置:
# 启用虚拟A/B
AB_OTA_UPDATER := true
# 启用压缩快照
BOARD_VIRTUAL_AB_COMPRESS := true
# 压缩算法:lz4, gzip, zstd 可选
BOARD_VIRTUAL_AB_COMPRESS_METHOD := lz4
# 压缩级别:1-9,默认3
BOARD_VIRTUAL_AB_COMPRESS_LEVEL := 3
# 快照存储路径
BOARD_VIRTUAL_AB_COMPRESS_PATH := /data/gsi/
嗯,这里有个坑。我曾经把BOARD_VIRTUAL_AB_COMPRESS_LEVEL设成了9,想着压缩率高一点。结果升级时间从2分钟变成了8分钟。后来查资料才发现,lz4的级别9和级别3,压缩比只差了不到5%,但时间多了4倍。得不偿失。
合并阶段的细节
合并阶段是VABC最容易被忽视的地方。系统重启后,init进程会检查是否有待合并的快照。如果有,它会启动一个叫snapshotctl的服务来执行合并。
合并过程是增量式的。每次合并一个块,然后更新快照的元数据。如果合并过程中断电,下次启动时会从上次中断的地方继续。这就是所谓的“断点续传”。
我记得有一次,测试同事反馈说设备升级后重启卡在开机动画。查了半天,发现是合并过程中/data分区空间不足。原来COW文件虽然压缩了,但合并时需要临时解压空间。如果/data剩余空间小于COW文件大小的两倍,合并就会失败。
避坑指南:我曾经因为/data分区剩余空间不足,导致VABC合并失败,设备卡在recovery模式。建议在OTA升级前,检查/data剩余空间是否大于COW文件预估大小的2倍。可以在update_engine的预检查阶段加一个空间校验。
VABC的优缺点总结
说了这么多,我总结一下VABC的优缺点:
- 优点:
- 节省存储空间,压缩比可达2:1到4:1
- 支持回滚,升级失败可恢复
- 合并过程支持断点续传
- 兼容现有A/B升级流程
- 缺点:
- 增加CPU开销,升级时间变长
- 合并阶段依赖/data分区空间
- 调试难度大,压缩快照出问题时不好定位
- 对低端设备不友好,CPU太弱会导致升级卡顿
个人建议:如果你的设备存储大于128GB,其实没必要上VABC。传统虚拟A/B就够用了。VABC最适合64GB及以下的设备。另外,如果设备CPU是Cortex-A53级别的,建议慎用zstd,lz4是更稳妥的选择。
最后说一句,VABC不是银弹。它用CPU时间换存储空间,这个trade-off你要想清楚。我在项目上见过为了省500MB空间,结果升级时间翻倍的案例。嗯,设计系统就是这样,没有完美的方案,只有合适的取舍。
公众号:蓝海资料掘金营,微信deep3321