4. GKI镜像组成与构建产物

在 GKI(Generic Kernel Image)量产交付体系中,构建产物并不是“一个内核文件”那么简单。Android 启动链路把内核、通用内核模块、厂商模块、设备树覆盖、ramdisk 与动态分区系统镜像拆分到多个独立分区中,每个分区承担明确的职责,并通过 bootconfig、cmdline、DTBO 与 vendor 模块实现“通用内核 + 厂商定制”的解耦。理解这些镜像的组成、加载顺序与依赖关系,是后续签名、OTA、回滚与量产验证的基础。

一、核心原理:为什么 GKI 要把启动产物拆成多个镜像

传统 Android 内核把大量厂商驱动直接编译进 Image,导致内核与 SoC、板级配置强耦合,无法独立升级。GKI 的核心思路是:Google 提供稳定的 KMI(Kernel Module Interface)与通用内核镜像,厂商把非通用驱动做成可加载模块,放在 vendor_boot.img 或 vendor_dlkm 中;同时把与启动强相关的通用 ramdisk 与厂商 ramdisk 分离,分别放入 init_boot.img 与 vendor_boot.img。这样内核可以随 GKI 版本独立更新,而厂商模块与设备树保持相对稳定。

因此,量产构建产物通常包括:boot.img、init_boot.img、vendor_boot.img、dtbo.img、super.img 以及若干 .ko 内核模块。它们分别对应启动阶段的不同加载点,缺一不可。

GKI 启动镜像组成与加载链路 Bootloader 加载各分区 dtbo.img 设备树覆盖 boot.img GKI 内核 + 通用 ramdisk init_boot.img 通用 init ramdisk vendor_boot.img 厂商 ramdisk + vendor_dlkm + DTB/DTBO 附加段 super.img 动态分区:system / vendor / product / system_ext / vendor_dlkm 内核模块 .ko GKI 模块 + 厂商模块 按 KMI 校验后加载 加载顺序要点 1. Bootloader 先加载 dtbo.img 与 boot.img,建立设备树与 GKI 内核运行环境。 2. init_boot.img 提供通用 init 与 first-stage 挂载逻辑;vendor_boot.img 提供厂商 ramdisk 与 vendor_dlkm。 3. super.img 中的 vendor_dlkm 与 vendor_boot 中的模块按 KMI 校验后加载,最终挂载 system/vendor 等动态分区。

二、各镜像的组成与职责

boot.img:GKI 的核心启动镜像,包含通用内核 Image(或 Image.lz4)与通用 ramdisk。它由 Google 提供,KMI 稳定,厂商不应直接修改内核二进制。boot.img 的 header 中记录内核版本、cmdline、os_version 等元数据。

init_boot.img:从 Android 13 开始引入,专门存放通用 init ramdisk。它把原本位于 boot.img 中的 first-stage init 与 init.rc 拆分出来,使 GKI 内核与 init 可以独立升级。量产中必须保证 init_boot 与 boot 的版本匹配。

vendor_boot.img:厂商定制启动镜像,包含厂商 ramdisk、vendor_dlkm 模块、DTB/DTBO 附加段以及 vendor bootconfig。它承载 SoC 与板级差异,是 GKI 解耦的关键。

dtbo.img:设备树覆盖(Device Tree Overlay)镜像,用于在通用 DTB 基础上叠加板级硬件描述。多板型项目常通过 dtbo 的 id 与 board_id 匹配选择正确覆盖。

super.img:动态分区容器,内部包含 system、vendor、product、system_ext、vendor_dlkm 等逻辑分区。量产时通过 lpmake 生成,支持 OTA 动态调整分区大小。

内核模块 .ko:分为 GKI 模块与厂商模块。GKI 模块随内核发布,厂商模块放在 vendor_dlkm 或 vendor_boot 中。加载前必须通过 KMI 符号校验,否则会拒绝加载。

三、构建流程与关键命令

典型 GKI 量产构建流程如下:先编译 GKI 内核与模块,再编译厂商模块,随后打包各镜像,最后生成 super.img 并签名。

# 1. 编译 GKI 内核与模块
tools/bazel build //common:kernel_aarch64_dist

# 2. 编译厂商模块(以 vendor_dlkm 为例)
make -j$(nproc) vendor_dlkm

# 3. 打包 boot.img / init_boot.img / vendor_boot.img
mkbootimg --kernel Image.lz4 \
  --ramdisk init_boot_ramdisk.img \
  --header_version 4 \
  --output boot.img

# 4. 生成 dtbo.img
mkdtboimg create dtbo.img board-*.dtbo

# 5. 生成 super.img
lpmake --device-size 6442450944 \
  --partition system:readonly:2147483648:system.img \
  --partition vendor:readonly:1073741824:vendor.img \
  --partition vendor_dlkm:readonly:536870912:vendor_dlkm.img \
  --output super.img
构建产物必须与 build.config、KMI 符号列表、ABL 版本保持一致。量产前建议用 avbtool 校验各镜像的 AVB 签名与 hash。

四、量产场景与常见误区

在量产交付中,最常见的误区是“只更新 boot.img 而忽略 init_boot 与 vendor_boot 的版本匹配”。GKI 升级时,boot.img 的内核版本变化,若 init_boot 仍是旧版,first-stage init 可能无法识别新的 bootconfig,导致启动卡在 bootloader 或 init 阶段。另一个误区是把厂商模块直接编译进内核,破坏 KMI 稳定性,导致后续 GKI 升级无法复用模块。

复盘建议:建立镜像版本矩阵,记录每个量产版本的 boot、init_boot、vendor_boot、dtbo、super 与模块的 hash 和 KMI 版本;在 OTA 包中同时携带这些镜像,避免“部分升级”造成启动失败。

镜像/产物 主要组成 是否随 GKI 升级 量产关注点
boot.img GKI 内核 + 通用 ramdisk 是 KMI 版本、AVB 签名
init_boot.img 通用 init ramdisk 是 与 boot 版本匹配
vendor_boot.img 厂商 ramdisk + vendor_dlkm + DTB 否(随厂商) board_id、模块依赖
dtbo.img 设备树覆盖 否 多板型 id 匹配
super.img system/vendor/product/vendor_dlkm 部分 动态分区大小、lpmake 参数
内核模块 .ko GKI 模块 + 厂商模块 部分 KMI 符号校验、加载顺序
量产版本禁止混用不同 KMI 版本的 boot.img 与 vendor_dlkm。若 KMI 不匹配,内核会拒绝加载厂商模块,表现为开机后外设不可用或直接 kernel panic。

掌握这些镜像的组成与依赖关系后,后续的签名、OTA 打包、回滚策略与量产验证才能建立在正确的分区模型之上。GKI 的“通用内核 + 厂商模块 + 动态分区”结构,本质上是用清晰的边界换取长期可升级性,而每一个镜像都是这条链路上不可替代的一环。