4、Android分区与镜像:各分区的作用、镜像格式、分区表与动态分区

Android系统的OTA升级本质上是针对设备存储中特定分区的数据更新操作。要深入理解OTA升级的机制、脚本编写和问题排查,首先必须对Android设备的存储布局、各分区的功能定位以及镜像文件的组织格式有清晰的认知。本章将系统梳理Android系统中关键分区的作用、raw与sparse镜像格式的区别、分区表的发展演变,以及动态分区(Dynamic Partition)技术对OTA升级流程带来的深刻影响。

Android 分区与镜像核心概念关系图 分区表(Partition Table) 传统固定分区( / MBR) 动态分区(super 分区) 固定分区示例: boot / system / vendor / recovery misc / cache / userdata / dtbo vbmeta / persist / metadata 特点:分区大小固定,布局简单 super 分区内包含: system / vendor / product / odm system_ext / vendor_dlkm / odm_dlkm 逻辑分区大小可动态调整 特点:灵活分配,支持OTA扩容 镜像格式(Image Format) Raw 原始镜像 逐字节写入,无压缩,大小固定 Sparse 稀疏镜像 跳过空洞,压缩传输,适合OTA OTA 升级 = 分区表 + 镜像格式 + 升级策略 的综合工程

4.1 核心分区的作用与职责

Android设备在出厂时,其eMMC或UFS存储芯片会被划分为多个独立的分区。每个分区承载特定的功能,OTA升级时,系统根据升级包中的脚本指令,精准地擦写目标分区。理解每个分区的职责,是判断升级失败后哪个环节出错的基础。

4.1.1 关键分区功能详解

以下是Android设备中与系统启动和OTA升级直接相关的核心分区及其作用:

分区名称挂载点/位置核心作用OTA升级关注点
boot无(由Bootloader加载)存放Linux内核(zImage/Image)和ramdisk(根文件系统)。负责初始化硬件、挂载system分区。升级内核时需整体替换;若boot损坏,设备将无法启动到Android系统。
system/system存放Android操作系统核心框架、系统应用、库文件(.so)等只读系统文件。OTA升级的主要目标分区,通常以只读方式挂载,升级时需先卸载或remount。
vendor/vendor存放硬件相关库、HAL(硬件抽象层)、固件、厂商专属配置。用于将硬件驱动与AOSP框架解耦。升级硬件驱动或HAL时更新;vendor与system版本需匹配,否则可能导致系统服务崩溃。
recovery无(独立启动)存放恢复模式的内核和ramdisk。用于执行OTA升级、恢复出厂设置、日志抓取等。升级recovery分区本身需谨慎;A/B设备通常将recovery作为boot的一部分。
misc/misc存储启动控制信息,如bootloader message、OTA升级状态标志、恢复模式指令等。OTA升级前会写入升级指令;若misc分区损坏,可能导致设备无法进入recovery或误启动。
cache/cache存放临时文件、OTA升级包(非A/B设备)、系统日志。用于恢复模式下的数据缓存。非A/B设备升级时,OTA包先下载至cache;空间不足会导致升级失败。
userdata/data存放用户安装的应用、用户数据、设置等。加密存储。OTA升级通常不擦除userdata,但升级失败回滚时可能触发数据清除。
dtbo无存放设备树覆盖(Device Tree Blob Overlay),用于硬件配置的动态调整。升级dtbo需与boot中的内核匹配,否则可能导致设备无法识别外设。
vbmeta无存放Verified Boot元数据,用于校验boot、system、vendor等分区的完整性。OTA升级后需更新vbmeta哈希;若校验失败,设备将进入红色警告状态。

核心原理:OTA升级的本质是“分区级”的增量或全量更新。系统升级服务(update_engine或recovery)根据升级脚本(updater-script)中的指令,对上述分区进行精确的读写操作。例如,增量升级时,系统会读取旧分区中的特定数据块,与升级包中的patch数据进行合并,生成新数据后写回分区。

4.1.2 分区与启动流程的关联

分区的设计直接决定了设备的启动链路。典型的启动流程为:Bootloader → boot分区(内核+ramdisk)→ system分区(init进程)→ vendor分区(HAL服务)→ zygote → SystemServer。OTA升级时,如果boot或system分区更新失败,设备将无法完成启动,此时recovery分区或misc分区中的标志位将引导设备进入恢复模式进行修复。

4.2 镜像格式:raw与sparse

在生成OTA升级包或刷机包时,分区内容会被打包成特定格式的镜像文件。理解raw和sparse格式的区别,对于控制升级包大小、优化写入速度至关重要。

4.2.1 Raw镜像格式

Raw镜像是对分区数据的逐字节拷贝,不包含任何压缩或空洞优化。例如,一个1GB的system分区,即使实际只使用了600MB,其raw镜像大小也是1GB。这种格式的优点是结构简单,写入时可直接使用dd命令进行块设备拷贝;缺点是文件体积大,不适合网络传输。

# 生成raw镜像示例
dd if=/dev/block/bootdevice/by-name/system of=/tmp/system_raw.img bs=4096
# 查看镜像大小(与分区大小一致)
ls -lh /tmp/system_raw.img

4.2.2 Sparse镜像格式

Sparse镜像(稀疏镜像)是Android系统中广泛使用的格式,其核心思想是跳过分区中的“空洞”(即全零或未使用的数据块),只记录有效数据块的位置和内容。Sparse镜像文件头部包含一个描述符,用于记录原始分区的总大小、块大小以及有效块的映射表。

对比维度Raw 镜像Sparse 镜像
文件大小与分区大小完全一致通常远小于分区大小(取决于数据密度)
写入方式直接逐块写入需解析头部描述符,按映射表写入有效块
适用场景本地快速刷机(fastboot)OTA网络传输、recovery模式写入
生成工具dd、catimg2simg、simg2img
校验方式直接计算整个文件哈希需先转换为raw格式或按块计算哈希

实战经验:在制作OTA增量包时,服务器端会将新旧system分区分别转换为sparse格式,然后逐块对比,生成差分补丁(bsdiff/imgdiff)。由于sparse格式跳过了空洞,差分算法只需处理实际有数据的块,显著减少了补丁包的大小。例如,一个1.5GB的system分区,raw格式的增量包可能达到800MB,而sparse格式的增量包可能仅需200MB。

4.2.3 镜像格式转换与校验

在OTA升级脚本中,经常需要将sparse镜像转换为raw格式后再写入分区,或者反之。Android提供了simg2img和img2simg工具。

# 将sparse镜像转换为raw镜像
simg2img system_sparse.img system_raw.img

# 将raw镜像转换为sparse镜像
img2simg system_raw.img system_sparse.img 4096

# 校验镜像完整性(使用sha1sum)
sha1sum system_sparse.img

4.3 分区表与动态分区技术

随着Android系统功能的增加,system、vendor等分区的大小需求不断变化。传统的固定分区表()在出厂时即确定各分区大小,无法灵活调整。动态分区(Dynamic Partition)技术应运而生,它允许在设备运行时动态调整逻辑分区的大小。

4.3.1 传统分区表的局限性

在Android 10之前的设备上,分区表在工厂烧录时即被固化。例如,某设备出厂时system分区为2GB,vendor分区为1GB。当后续OTA升级需要更大的system分区时,由于相邻分区(如vendor或cache)的大小无法自动缩减,升级将面临空间不足的风险。此外,不同厂商的硬件配置差异导致分区表碎片化严重,增加了OTA兼容性维护成本。

4.3.2 动态分区的架构与优势

动态分区将多个物理分区(如system、vendor、product、odm)合并到一个名为super的物理分区中。super分区内部通过逻辑卷管理器(类似LVM)创建多个逻辑分区。这些逻辑分区的大小可以在OTA升级时通过update_engine或recovery中的resize2fs命令动态调整。

# 查看动态分区信息(在设备上执行)
lsblk /dev/block/by-name/super
# 输出示例:
# super        259:0    0   4G  0 disk
# ├─system     259:1    0   2.5G 0 part /system
# ├─vendor     259:2    0   1G   0 part /vendor
# └─product    259:3    0   0.5G 0 part /product

动态分区的核心优势在于:

  • 空间利用率高:逻辑分区大小可随OTA包需求动态伸缩,无需预留大量空闲空间。
  • 升级更可靠:A/B无缝升级与动态分区结合,可在后台完成分区调整和新系统写入,降低升级失败风险。
  • 简化分区表:物理分区数量减少,分区表更简洁,降低Bootloader复杂度。

4.3.3 动态分区对OTA升级流程的影响

在动态分区设备上,OTA升级脚本不再直接操作/dev/block/bootdevice/by-name/system,而是通过update_engine的DynamicPartitionControl接口来管理逻辑分区。升级流程变为:

  1. 解析OTA包中的dynamic_partition_info文件,获取目标逻辑分区的大小和哈希值。
  2. 通过dmctl或liblp库调整super分区内的逻辑分区大小。
  3. 将新镜像写入对应的逻辑分区(通常为sparse格式)。
  4. 更新super分区头部的元数据,使新分区布局生效。

风险提示:动态分区虽然灵活,但引入了额外的抽象层。若super分区的元数据损坏,或逻辑分区调整过程中断电,可能导致所有逻辑分区无法访问,设备变砖。因此,OTA升级时必须确保电池电量充足,且升级包中的update_engine版本与设备固件兼容。

4.4 案例拆解:一次system分区扩容的OTA升级

以下是一个典型的动态分区设备OTA升级案例,展示了从问题发现到成功升级的完整闭环。

背景与约束:某款Android 11设备,出厂时super分区为6GB,其中system逻辑分区为2.5GB,vendor为1.5GB,product为1GB,剩余1GB为free空间。新版本系统因增加GMS套件,system分区需要额外800MB空间。

初始现象:用户收到OTA推送后,下载完成进入升级阶段,但升级进度条在约60%时停滞,随后设备重启并提示“系统更新失败,无法启动”。

关键日志分析:抓取/cache/recovery/log,发现如下关键错误:

I:UpdateEngine: Attempting to resize partition "system" from 2621440 KB to 3440640 KB
E:UpdateEngine: Not enough free space in super partition. Required: 819200 KB, Available: 1048576 KB
E:UpdateEngine: Dynamic partition resize failed for "system"

分析判断过程:日志显示,虽然super分区有1GB空闲空间,但系统要求system分区增加800MB,而当前空闲空间为1GB,理论上足够。但进一步检查发现,product分区和vendor分区之间存在碎片化,且super分区头部元数据中预留的group(组)大小限制了system所在组的最大容量。该设备的super分区被划分为两个group:default组(包含system和product)和vendor组。default组的最大容量被设定为3.5GB,而system扩容后需要3.44GB,加上product的1GB,已超过组上限。

实际操作步骤:

  1. 联系厂商获取新的OTA包,该包中修改了dynamic_partition_info,将default组的最大容量调整为4.5GB。
  2. 在升级脚本中增加了resize_group命令,在调整system分区前先扩展default组。
  3. 重新生成OTA包并推送给用户。

结果对比:升级成功后,执行df -h /system显示system分区大小为3.3GB,可用空间充足。设备启动正常,GMS套件运行流畅。

风险与代价:由于调整了组大小,升级过程中super分区元数据被重写。若在写入元数据时断电,可能导致分区表损坏。因此,该OTA包强制要求电池电量高于50%,且升级过程中禁止强制关机。

复盘经验:动态分区设备的OTA升级,不仅要关注单个分区的大小,还要关注组(group)的容量限制。在制作OTA包时,应使用lpdump工具检查当前设备的super分区布局,确保新分区大小在组限制内。

总结复盘:本章节深入剖析了Android分区的职责、镜像格式的差异以及动态分区的原理。对于OTA开发工程师而言,掌握这些知识意味着:

  • 能够快速定位升级失败是分区空间不足、镜像格式错误还是分区表不匹配。
  • 在编写升级脚本时,能正确选择raw或sparse格式,并合理使用resize2fs、simg2img等工具。
  • 面对动态分区设备,能理解super分区的逻辑结构,避免因组容量限制导致升级失败。