3. Android 内核版本体系

在 Android 生态中,“内核版本”并不是一个单一概念。同一个设备上运行的内核,往往同时涉及 ACK(Android Common Kernel)、AOSP 内核、GKI 内核 以及 供应商内核 四个层次。理解它们之间的区别与联系,是进行 GKI 版本迁移和兼容调试的前提。很多工程师在迁移时遇到“内核版本对不上”“模块加载失败”“KMI 符号缺失”等问题,根源往往在于没有厘清这四者的边界与依赖关系。

一、四个内核概念的核心定义

ACK(Android Common Kernel)是 Google 在 Linux 长期支持版本(LTS)基础上维护的 Android 通用内核分支。它包含 Android 特有的补丁集,例如 binder、ashmem、low memory killer、wakelock 等,是 Android 内核功能的“公共基线”。ACK 按内核版本和 Android 版本形成多个分支,例如 android12-5.10、android13-5.15、android14-6.1。

AOSP 内核通常指 AOSP 代码树中与内核相关的部分,包括内核源码的引用、预编译内核镜像、内核模块以及构建脚本。AOSP 内核更强调“Android 开源项目所发布或引用的内核产物”,它可能是 ACK 的某个具体提交,也可能是 GKI 的预编译镜像。

GKI 内核(Generic Kernel Image)是 Google 从 Android 11 开始推行的通用内核镜像方案。GKI 将内核核心与供应商模块解耦:核心内核由 Google 统一编译和发布,供应商特有的驱动以模块形式加载。GKI 内核必须满足 KMI(Kernel Module Interface)稳定性要求,确保同一 KMI 版本下模块可跨内核小版本复用。

供应商内核是 SoC 厂商或设备厂商基于 ACK/GKI 定制的内核,包含特定硬件平台的驱动、电源管理、时钟、GPIO、摄像头、显示等补丁。在 GKI 之前,供应商内核通常是完整的内核源码树;在 GKI 之后,供应商代码应尽量以模块形式存在,减少对核心内核的侵入。

二、四者关系与层次结构

从依赖关系看,Linux LTS 是最底层基线,ACK 在其上叠加 Android 补丁,GKI 则从 ACK 中选取并固化 KMI,AOSP 内核是 GKI 在 AOSP 构建体系中的呈现,供应商内核则是在 GKI 基础上加载供应商模块后的最终运行形态。可以用下面的结构图表示:

Android 内核版本体系层次关系 Linux LTS 长期支持内核 5.10 / 5.15 / 6.1 / 6.6 … ACK(Android Common Kernel) Android 补丁集:binder / ashmem / LMK / wakelock / 调度优化 GKI 内核(Generic Kernel Image) 核心内核 + KMI 稳定接口 + 预编译镜像 KMI 版本:android12-5.10 / android13-5.15 / android14-6.1 AOSP 内核 AOSP 构建体系中的内核镜像、模块与构建脚本 供应商内核(Vendor Kernel) SoC / 设备驱动模块:显示、摄像头、电源、时钟、GPIO、存储 Android 特性基线 Google 维护 KMI 稳定 模块跨版本复用 Google 统一发布 厂商定制 模块化加载

三、关键区别对比

为了更清晰地辨别四者,下表从维护者、代码范围、发布形式、KMI 约束和迁移影响五个维度进行对比:

维度 ACK AOSP 内核 GKI 内核 供应商内核
维护者 Google Android 内核团队 AOSP 项目 Google GKI 团队 SoC 厂商 / OEM
代码范围 LTS + Android 补丁 AOSP 引用的内核产物 核心内核 + KMI 符号表 平台驱动 + 板级配置
发布形式 源码分支 预编译镜像 / 源码 预编译 Image + 模块 模块 .ko + 设备树
KMI 约束 无强制 KMI 跟随 GKI 严格 KMI 稳定 必须匹配 GKI KMI
迁移影响 决定功能基线 决定构建方式 决定模块兼容性 决定硬件适配

四、迁移场景中的实际案例

假设某项目需要从 Android 12(内核 5.10)迁移到 Android 14(内核 6.1)。迁移前,设备使用的是 android12-5.10 的 ACK 分支,供应商内核以完整源码树方式集成。迁移到 GKI 后,核心内核变为 android14-6.1 的 GKI 镜像,供应商驱动必须改为模块形式,并确保使用的符号在 KMI 白名单中。

# 查看当前内核版本与 GKI 信息
adb shell uname -a
# Linux localhost 5.10.107-android12-9-00001-g1234567890ab #1 SMP PREEMPT

# 查看 KMI 版本
adb shell cat /proc/version
# Linux version 5.10.107-android12-9-00001 (build@abfarm) ...

# 查看已加载的供应商模块
adb shell lsmod | grep -E "vendor|display|camera"

# 检查模块符号依赖是否满足 KMI
adb shell cat /proc/kallsyms | grep "your_module_symbol"

如果迁移后模块加载失败,常见日志如下:

[   12.345678] your_driver: Unknown symbol reserved_memory_init (err -2)
[   12.345679] your_driver: module verification failed: signature and/or required key missing
[   12.345680] your_driver: disagrees about version of symbol module_layout

这些错误分别对应:符号不在 KMI 白名单、模块签名不匹配、模块与内核版本不一致。解决思路是:将符号加入 KMI 允许列表、使用正确的签名密钥、确保模块与 GKI 内核的 module_layout 版本一致。

常见误区:很多工程师认为“ACK 就是 GKI”,或者“AOSP 内核就是供应商内核”。实际上,ACK 是源码基线,GKI 是二进制发布形态,AOSP 内核是构建体系中的引用,供应商内核是最终运行时的定制部分。四者层次不同,不能混为一谈。

五、复盘与调试建议

在进行 GKI 迁移时,建议按以下顺序排查:

  1. 确认基线:明确当前 ACK 分支和 GKI KMI 版本,例如 android12-5.10 对应 KMI 5.10。
  2. 检查模块:使用 modinfo 查看模块的 vermagic 和依赖符号,确认与目标 GKI 内核匹配。
  3. 核对 KMI:对比 android/abi_gki_aarch64 符号列表,确认供应商模块使用的符号在白名单内。
  4. 验证签名:确保模块使用与 GKI 内核匹配的签名密钥,避免 module verification failed。
  5. 回归测试:在目标 GKI 内核上加载所有供应商模块,检查 dmesg 中是否有 Unknown symbol 或 disagrees about version。
实用命令:使用 adb shell dmesg | grep -i "kmi\|module\|symbol" 可以快速定位模块加载问题;使用 extract-ikconfig 可以提取内核配置,确认 GKI 内核是否开启了必要的选项。

总结来说,ACK 是 Android 内核的功能基线,AOSP 内核是构建体系中的呈现,GKI 内核是模块化与 KMI 稳定的核心,供应商内核是硬件适配的最终载体。四者层层递进、相互约束。只有厘清它们的关系,才能在 GKI 迁移中准确判断问题边界,避免把“版本不匹配”误判为“驱动 bug”,从而高效完成兼容调试。