Virtual A/B 概览

Virtual A/B 是 Android 的主要更新机制。Virtual A/B 基于旧版 A/B 更新(请参阅A/B 系统更新)和非 A/B 更新(在版本 15 中已弃用)构建而成,旨在减少更新的空间开销。

Virtual A/B 实际上没有用于动态分区的额外槽位,请参阅动态分区。而是将增量写入快照,然后在确认启动成功后合并到基本分区中。Virtual A/B 使用 Android 特定的快照格式。请参阅用于压缩快照的 COW 格式,该格式允许压缩快照并最大限度地减少磁盘空间使用量。在完整 OTA 中,通过压缩可将快照大小减少约 45%,增量 OTA 快照大小则减少约 55%。

Android 12 提供了 Virtual A/B 压缩选项,用于压缩快照分区。Virtual A/B 提供以下功能

  • Virtual A/B 更新与 A/B 更新一样都是无缝的(更新完全在设备运行时在后台进行)。Virtual A/B 更新最大限度地缩短了设备离线和无法使用的时间。
  • Virtual A/B 更新可以回滚。如果新的操作系统无法启动,设备会自动回滚到之前的版本。
  • Virtual A/B 更新通过仅复制启动加载程序使用的分区来使用最少的额外空间。其他可更新分区均已快照

背景和术语

本部分定义了术语并描述了支持 Virtual A/B 的技术。在 OTA 安装期间,新的操作系统数据会写入其物理分区的新槽位,或写入 Android 特定的 COW 设备。设备重启后,动态分区数据将通过使用 dm-user 和 snapuserd 守护程序合并回其基本设备。此过程完全在用户空间中进行。

Device-mapper

Device-mapper 是 Android 中常用的 Linux 虚拟块层。借助动态分区/system 等分区是分层设备的堆栈

  • 堆栈的底部是物理超级分区(例如,/dev/block/by-name/super)。
  • 中间是一个 dm-linear 设备,用于指定超级分区中的哪些块构成给定的动态分区。在 A/B 设备上,这显示为 /dev/block/mapper/system_[a|b],在非 A/B 设备上,这显示为 /dev/block/mapper/system
  • 顶层存在一个 dm-verity 设备,专为验证分区而创建。此设备验证 dm-linear 设备上的块是否已正确签名。它显示为 /dev/block/mapper/system-verity,并且是 /system 挂载点的来源。

图 1 显示了 /system 挂载点下的堆栈是什么样的。

Partition stacking underneath
system

图 1. /system 挂载点下的堆栈

压缩快照

在 Android 12 及更高版本中,由于 /data 分区上的空间需求可能很高,因此您可以在构建中启用压缩快照,以解决 /data 分区更高的空间需求。

Virtual A/B 压缩快照构建于以下 Android 12 及更高版本中提供的组件之上

  • dm-user,一个类似于 FUSE 的内核模块,允许用户空间实现块设备。
  • snapuserd,一个用户空间守护进程,用于实现新的快照格式。

这些组件实现了压缩。为实现压缩快照功能而做的其他必要更改在接下来的章节中给出:压缩快照的 COW 格式dm-usersnapuserd

压缩快照的 COW 格式

在 Android 12 及更高版本中,压缩快照使用 Android 特定的 COW 格式。COW 格式包含有关 OTA 的元数据,并具有包含 COW 操作和新操作系统数据的不同缓冲区。与仅允许替换操作的内核快照格式相比(将基本映像中的块X替换为快照中块Y的内容),Android 压缩快照 COW 格式更具表现力,并支持以下操作

  • 复制:基本设备中的块X应替换为基本设备中的块Y
  • 替换:基本设备中的块X应替换为快照中块Y的内容。这些块都经过 gz 压缩。
  • :基本设备中的块X应替换为全零。
  • XOR:COW 设备存储块X和块Y之间 XOR 压缩的字节。(在 Android 13 及更高版本中可用。)

完整 OTA 更新仅包含替换操作。增量 OTA 更新还可以包含复制操作。

磁盘上的完整快照布局如下所示

cow format

图 2. 磁盘上的 Android COW 格式

dm-user

dm-user 内核模块使 userspace 能够实现设备映射器块设备。dm-user 表条目在 /dev/dm-user/<control-name> 下创建一个杂项设备。userspace 进程可以轮询该设备以接收来自内核的读取和写入请求。每个请求都有一个关联的缓冲区,供用户空间填充(对于读取)或传播(对于写入)。

dm-user 内核模块为内核提供了一个新的用户可见接口,该接口不属于上游 kernel.org 代码库。在它成为上游代码库之前,Google 保留在 Android 中修改 dm-user 接口的权利。

snapuserd

dm-usersnapuserd 用户空间组件实现了 Virtual A/B 压缩。Snapuserd 是一个用户空间守护进程,负责写入和读取 Android COW 设备。所有到快照的 I/O 都必须通过此服务。在 OTA 安装期间,新的操作系统数据由 snapuserd(使用压缩)写入快照。元数据的解析和新块数据的解包也在此处处理。

XOR 压缩

对于搭载 Android 13 及更高版本的设备,默认启用的 XOR 压缩功能使用户空间快照能够存储旧块和新块之间 XOR 压缩的字节。当 Virtual A/B 更新中仅更改块中的少量字节时,XOR 压缩存储方案使用的空间比默认存储方案少,因为快照不存储完整的 4K 字节。快照大小的减小是可能的,因为 XOR 数据包含许多零,并且比原始块数据更容易压缩。在 Pixel 设备上,XOR 压缩将快照大小减少 25% 到 40%。

对于升级到 Android 13 及更高版本的设备,必须启用 XOR 压缩。有关详细信息,请参阅 XOR 压缩

快照合并

对于搭载 Android 13 及更高版本的设备,Virtual A/B 压缩中的快照和快照合并过程由 snapuserd 用户空间组件执行。对于升级到 Android 13 及更高版本的设备,必须启用此功能。有关详细信息,请参阅 用户空间合并

以下描述了 Virtual A/B 压缩过程

  1. 框架从 dm-verity 设备挂载 /system 分区,该设备堆叠在 dm-user 设备之上。这意味着来自根文件系统的每个 I/O 都路由到 dm-user
  2. dm-user 将 I/O 路由到用户空间 snapuserd 守护进程,该守护进程处理 I/O 请求。
  3. 当合并操作完成时,框架将 dm-verity 折叠到 dm-linear (system_base) 之上,并删除 dm-user

Virtual A/B compression
process

图 3. Virtual A/B 压缩过程

快照合并过程可能会中断。如果在合并过程中重新启动设备,则合并过程将在重新启动后恢复。

Init 转换

当使用压缩快照启动时,第一阶段 init 必须启动 snapuserd 以挂载分区。这带来了一个问题:当加载和强制执行 sepolicy 时,snapuserd 被置于错误的上下文中,并且其读取请求失败,并出现 selinux 拒绝。

为了解决这个问题,snapuserdinit 同步转换,如下所示

  1. 第一阶段 init 从 ramdisk 启动 snapuserd,并将指向它的打开文件描述符保存在环境变量中。
  2. 第一阶段 init 将根文件系统切换到系统分区,然后执行 init 的系统副本。
  3. init 的系统副本将组合的 sepolicy 读取到字符串中。
  4. Init 在所有 ext4 支持的页面上调用 mlock()。然后,它停用快照设备的所有设备映射器表,并停止 snapuserd。在此之后,禁止从分区读取数据,因为这样做会导致死锁。
  5. 使用指向 snapuserd 的 ramdisk 副本的打开描述符,init 使用正确的 selinux 上下文重新启动守护进程。快照设备的设备映射器表被重新激活。
  6. Init 调用 munlockall()- 再次执行 IO 是安全的。

空间使用情况

下表提供了使用 Pixel 的操作系统和 OTA 大小的不同 OTA 机制的空间使用情况比较。

大小影响 非 A/B A/B Virtual A/B Virtual A/B(压缩)
原始出厂映像 4.5GB super (3.8G 映像 + 700M 保留空间)1 9GB super(3.8G + 700M 保留空间,用于两个槽位) 4.5GB super (3.8G 映像 + 700M 保留空间) 4.5GB super (3.8G 映像 + 700M 保留空间)
其他静态分区 /cache
OTA 期间的额外存储空间(应用 OTA 后返回的空间) /data 上 1.4GB 0 /data 上 3.8GB2 /data 上 2.1GB2
应用 OTA 所需的总存储空间 5.9GB3 (super 和 data) 9GB (super) 8.3GB3 (super 和 data) 6.6GB3 (super 和 data)

1表示基于 Pixel 映射的假设布局。

2假设新的系统映像与原始映像大小相同。

3空间需求是临时的,直到重新启动。

Android 11 Virtual A/B

Android 11 的 Virtual A/B 使用内核 COW 格式写入动态分区。这最终被弃用,因为内核 COW 格式不支持压缩。

Android 12 Virtual A/B

在 Android 12 中,以 Android 特定 COW 格式的形式支持压缩。此版本的 Virtual A/B 需要将 Android 特定 COW 格式转换为内核 COW 格式。最终,这在 Android 13 中被取代,Android 13 取消了对内核 COW 格式和 dm-snapshot 的依赖。

要实现 Virtual A/B 或使用压缩快照功能,请参阅 实现 Virtual A/B