虚拟 A/B 概述

Android 有两种更新机制:A/B(无缝)更新和非 A/B 更新。为了降低代码复杂度并增强更新过程,在 Android 11 中,这两种机制通过虚拟 A/B 统一起来,以最小化存储成本为所有设备带来无缝更新。 Android 12 提供了虚拟 A/B 压缩选项来压缩快照分区。在 Android 11 和 Android 12 中,以下内容均适用:

  • 虚拟 A/B 更新与 A/B 更新一样无缝。虚拟 A/B 更新可最大限度地减少设备脱机和不可用的时间。
  • 虚拟 A/B 更新可以回滚。如果新操作系统无法启动,设备会自动回滚到之前的版本。
  • 虚拟 A/B 更新通过仅复制引导加载程序使用的分区来使用最少的额外空间。其他可更新分区被快照

背景和术语

本节定义了术语并描述了支持虚拟 A/B 的技术。

设备映射器

Device-mapper 是 Android 中经常使用的 Linux 虚拟块层。使用动态分区,像/system这样的分区是一堆分层设备:

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

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

Partition stacking underneath system

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

dm-快照

虚拟 A/B 依赖于dm-snapshot ,这是一个设备映射器模块,用于对存储设备的状态进行快照。使用dm-snapshot时,有四个设备在运行:

  • 基本设备是快照的设备。在此页面上,基本设备始终是动态分区,例如系统或供应商。
  • 写时复制(COW) 设备,用于记录对基本设备的更改。它可以是任意大小,但必须足够大以适应对基本设备的所有更改。
  • 快照设备是使用snapshot目标创建的。对快照设备的写入将写入 COW 设备。从快照设备读取从基本设备或 COW 设备读取,具体取决于正在访问的数据是否已被快照更改。
  • 设备是使用snapshot-origin目标创建的。直接从基础设备读取到源设备。写入源设备直接写入基础设备,但通过写入COW设备备份原始数据。

Device mapping for dm-snapshot

图 2. dm-snapshot 的设备映射

压缩快照

在 Android 12 中,由于/data分区的空间要求可能很高,您可以在构建中启用压缩快照来解决/data分区的更高空间要求。

虚拟 A/B 压缩快照基于 Android 12 中可用的两个新组件构建:

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

这些组件启用压缩。为实现压缩快照功能所做的其他必要更改将在下一节中给出:压缩快照的 COW 格式dm-userSnapuserd

压缩快照的 COW 格式

在 Android 12 中,压缩快照使用新的 COW 格式。与用于未压缩快照的内核内置格式类似,压缩快照的 COW 格式具有元数据和数据的交替部分。原始格式的元数据只允许“替换”操作:将基础映像中的块X替换为快照中块Y的内容。压缩快照 COW 格式更具表现力,支持三种操作:

  • 复制- 基础设备中的块X应替换为基础设备中的块Y。
  • 替换- 基础设备中的块X应替换为快照中块Y的内容。这些块中的每一个都经过 gz 压缩。
  • - 基本设备中的块X应替换为全零。

完整的 OTA 更新仅包括替换归零操作。增量 OTA 更新还可以具有复制操作。

Android 12 中的 dm 用户

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

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

快照用户

dm-usersnapuserd用户空间组件实现了虚拟 A/B 压缩。

在 Virtual A/B 的未压缩版本中(在 Android 11 及更低版本中,或在没有压缩快照选项的 Android 12 中),COW 设备是原始文件。启用压缩后,COW 将作为dm-user设备运行,该设备连接到snapuserd守护程序的实例。

内核不使用新的 COW 格式。所以snapuserd组件在 Android COW 格式和内核内置格式之间转换请求:

Snapuserd component translating requests between Android COW format and kernel built-in format

图 3. snapuserd 作为 Android 和 Kernel COW 格式之间的转换器的流程图

这种转换和解压缩永远不会发生在磁盘上。 snapuserd组件拦截发生在内核中的 COW 读取和写入,并使用 Android COW 格式实现它们。

虚拟 A/B 压缩过程

这些部分提供了有关虚拟 A/B 压缩中使用的过程的详细信息:读取元数据、合并和执行初始化转换。

读取元数据

元数据由snapuserd守护进程构建。元数据主要是 2 个 ID 的映射,每个 8 字节,表示要合并的扇区。在dm-snapshot中,它被称为disk_exception

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

当旧数据块被新数据替换时,会使用磁盘异常。

Snapuserd守护程序通过 COW 库读取内部 COW 文件,并为 COW 文件中存在的每个 COW 操作构造元数据。

创建 dm- dm- snapshot设备时,从内核中的dm-snapshot启动元数据读取。

下图提供了元数据构建的 IO 路径的时序图。

Sequence diagram, IO path for metadata construction

图 4.元数据构建中 IO 路径的序列流

合并

引导过程完成后,更新引擎会将插槽标记为引导成功,并通过将dm-snapshot目标切换到dm-snapshot-merge目标来启动合并。

dm-snapshot遍历元数据并为每个磁盘异常启动一个合并 IO。下面显示了合并 IO 路径的高级概述。

Merge IO path

图 5.合并 IO 路径概览

如果设备在合并过程中重新启动,则合并在下次重新启动时恢复,合并完成。

初始化转换

使用压缩快照启动时,第一阶段 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虚拟 A/B(压缩)
原厂图片4.5GB超级(3.8G图片+700M预留) 1 9GB 超级(3.8G + 700M 预留,两个插槽) 4.5GB超级(3.8G图片+700M预留) 4.5GB超级(3.8G图片+700M预留)
其他静态分区/缓存没有任何没有任何没有任何
OTA期间的额外存储(应用OTA后返回的空间) /data 1.4GB 0 /data 3.8GB 2 /data 2.1GB 2
应用 OTA 所需的总存储空间5.9GB 3 (超级和数据) 9GB(超级) 8.3GB 3 (超级和数据) 6.6GB 3 (超级和数据)

1表示基于像素映射的假定布局。

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

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

要实施虚拟 A/B,或使用压缩快照功能,请参阅实施虚拟 A/B