1. 课程导入
Android GKI(Generic Kernel Image,通用内核镜像)是 Android 10 之后引入的内核交付模式,其核心目标是把 SoC 厂商、OEM 厂商与 Google 的内核改动解耦:Google 提供统一的内核镜像与稳定接口,厂商通过模块和 KMI 扩展硬件能力。学习本课程,不是简单记住几条编译命令,而是建立"镜像—模块—接口—验证"的完整工程视角。
一、学习目标:从会编译到会交付
完成本课程后,你应当能够独立完成一条 GKI 内核镜像的构建与验证链路,并理解每一步背后的约束。具体目标可归纳为四层:
- 概念层:说清 GKI、KMI、ABI、VINTF、boot.img、vendor_boot.img、init_boot.img 之间的关系与边界。
- 构建层:使用 Bazel/Kleaf 或 build.sh 构建 GKI 内核、内核模块与 boot 镜像,理解产物目录结构。
- 接口层:掌握 KMI 符号列表、ABI 监控、符号导出与模块签名,能判断一次改动是否破坏 ABI。
- 验证层:能通过 ABI 检查、模块加载、启动日志与 CTS/VTS 相关项定位问题。
核心判断:GKI 工程的成功标准不是"编译通过",而是"内核镜像可被多个 vendor 模块稳定加载,且 ABI 在版本周期内保持兼容"。
二、核心概念:一张图看清 GKI 的组成关系
GKI 把传统"一个内核打天下"的模式拆成了三层:Google 维护的通用内核镜像、厂商维护的 vendor 模块、以及两者之间的 KMI 契约。下图展示了镜像、模块、接口与启动分区之间的结构关系。
从图中可以看到,GKI 的关键不是"内核"本身,而是中间那层绿色的 KMI 契约。它既是 Google 对厂商的承诺,也是厂商必须遵守的边界。理解这一点,后续所有构建、调试与验证工作才有统一的判断标准。
三、整体框架与学习路径
本课程按"认知—构建—接口—验证—实战"的顺序展开,共 30 章。建议的学习路径如下:
- 认知阶段:理解 GKI 的由来、与 ACK 的关系、分区布局与启动流程。
- 构建阶段:搭建编译环境,掌握 Bazel/Kleaf 与 build.sh 两条构建路径,读懂产物目录。
- 接口阶段:深入 KMI 符号列表、ABI 监控、模块导出与签名机制。
- 验证阶段:使用 ABI 检查工具、模块加载测试、启动日志分析定位问题。
- 实战阶段:完成一次从源码到可启动镜像的完整交付,并处理典型 ABI 破坏案例。
| 阶段 | 关键产物 | 核心工具 | 验收标准 |
|---|---|---|---|
| 环境与源码 | repo 工作区、kernel/common | repo、git | 可同步并切换目标分支 |
| 内核构建 | Image、boot.img | Bazel、Kleaf、build.sh | 镜像可生成且版本号正确 |
| 模块构建 | *.ko、vendor_dlkm.img | Kbuild、modprobe | 模块可被内核加载 |
| ABI 验证 | abi.xml、symbol list | abi_gki_check、diff | 无未授权符号变更 |
| 启动验证 | dmesg、logcat | fastboot、adb | 系统正常启动且模块就绪 |
四、典型场景:一次模块加载失败如何定位
假设你在设备上执行 insmod vendor_dlkm.ko 失败,日志提示 Unknown symbol。这通常不是代码错误,而是 KMI 边界问题。可按以下顺序排查:
# 1. 查看内核版本与 KMI 版本
adb shell uname -r
adb shell cat /proc/version
# 2. 检查模块依赖的符号是否在 KMI 允许列表中
grep "your_symbol" android/abi_gki_aarch64.stg
# 3. 查看模块自身的符号需求
modinfo vendor_dlkm.ko | grep depends
# 4. 确认模块签名与内核是否匹配
adb shell dmesg | grep -i "module verification"
常见误区:把"编译通过"当成"可以交付"。GKI 工程中,ABI 破坏往往在编译阶段完全无感,只有到模块加载或 VTS 测试时才暴露。因此每次改动内核导出符号后,必须运行 ABI 检查,而不是等到集成阶段。
五、复盘:初学者最容易踩的三个坑
- 坑一:直接改内核源码树。GKI 内核由 Google 维护,厂商改动应通过模块或上游补丁进入,直接修改会导致后续无法同步。
- 坑二:忽视 KMI 符号列表。新增导出符号必须同步更新 symbol list 并通过 ABI 检查,否则模块在目标设备上无法加载。
- 坑三:混淆 boot 与 vendor_boot。通用内核与厂商 ramdisk 分属不同分区,调试时要先确认问题出在哪一段。