1. 课程导入
从 Android 5.0 引入 Treble 计划开始,Android 的底层软件架构经历了一次持续十余年的解耦演进。到 Android 11 正式落地 GKI(Generic Kernel Image,通用内核镜像),内核与厂商驱动的边界被重新划定:Google 负责统一内核镜像与 KMI(Kernel Module Interface)稳定性,芯片厂商与 OEM 则通过可加载模块和供应商分区完成差异化适配。这一变化直接影响了设备树(Device Tree)的组织方式与启动链路的适配策略,也是本课程要解决的核心问题。
一、为什么会有 GKI:背景与驱动力
在 GKI 之前,Android 设备的内核是"一机一内核":SoC 厂商把大量驱动、补丁、调试代码直接合入内核源码树,OEM 再叠加自己的改动。结果是每个设备的内核分支都不同,Google 的安全补丁无法统一下发,Android 版本升级往往需要重新移植整个内核,碎片化严重。
GKI 的核心思路是"内核与驱动分离":
- 通用内核镜像(GKI):由 Google 基于 ACK(Android Common Kernel)构建,所有设备共用同一份内核二进制,负责调度、内存管理、电源框架、核心子系统等通用能力。
- 供应商模块(Vendor Modules):SoC 与 OEM 的驱动以
.ko形式放在vendor_boot或vendor_dlkm分区,通过 KMI 与内核交互。 - KMI 稳定性:内核对外暴露的符号与结构被冻结,模块无需随内核小版本变化而重新编译。
- 启动镜像拆分:
boot分区承载 GKI 内核与通用 ramdisk,vendor_boot分区承载供应商 ramdisk、DTB 与供应商模块。
理解这条边界,是后续所有适配工作的前提:凡是通用能力,改内核;凡是硬件相关,改模块与设备树。
二、设备树与启动适配在 GKI 中的位置
设备树(Device Tree)用数据描述硬件,让同一份内核二进制能在不同主板上运行。在 GKI 架构下,设备树不再只放在内核里,而是分布在多个分区,并在启动的不同阶段被加载、叠加与合并。下图展示了从 SoC 上电到 Android 用户空间的关键链路,以及设备树与各分区的对应关系。
可以看到,设备树不再是"内核源码里的一个 dts 文件",而是横跨 boot、vendor_boot 两个分区、在启动早期被合并的运行时数据。适配工作的本质,就是让这条链路上的每一环都能正确衔接。
三、整体知识框架与实战路线
本课程按"认知 → 结构 → 适配 → 排障 → 复盘"的顺序展开,共 30 章,可归纳为五个模块:
| 模块 | 核心内容 | 典型产出 |
|---|---|---|
| 背景与架构 | GKI、KMI、Treble、分区布局 | 能画出启动链路与分区关系 |
| 设备树基础 | DTS/DTB/DTBO、overlay、compatible | 能读懂并修改板级设备树 |
| 启动适配 | boot/vendor_boot 构建、模块加载、cmdline | 能完成一次完整启动适配 |
| 调试与排障 | 串口日志、dmesg、模块加载失败分析 | 能定位启动卡死与驱动 probe 失败 |
| 工程化与复盘 | 版本管理、CI 校验、升级兼容 | 能建立可维护的适配流程 |
实战路线建议按以下顺序推进,每一步都要有可验证的结果:
- 搭建编译环境,能独立编译出 GKI 内核与 vendor 模块。
- 解包目标设备的
boot与vendor_boot,确认设备树与模块的实际内容。 - 修改一处设备树节点(如新增一个 I2C 设备),验证驱动能否 probe 成功。
- 制造一次典型故障(如删除模块依赖),用日志定位并修复。
- 整理成可复用的适配清单与检查脚本。
四、典型场景与常见误区
以一个真实场景为例:某设备升级到 GKI 后,触摸屏在开机后无响应。排查路径通常是:
# 1. 确认模块是否加载
adb shell lsmod | grep touch
# 2. 查看内核日志中的 probe 结果
adb shell dmesg | grep -i "touch\|i2c"
# 3. 检查设备树节点是否存在
adb shell ls /proc/device-tree/ | grep touch
# 4. 对比 compatible 字符串
adb shell cat /proc/device-tree/soc/i2c@1c00000/touch@38/compatible
常见误区集中在三点:
- 把设备树当成内核源码的一部分:在 GKI 下,很多板级设备树在
vendor_boot中,改错位置会导致"改了没生效"。 - 忽略 KMI 版本约束:模块必须与内核的 KMI 版本匹配,跨版本直接拷贝
.ko往往加载失败。 - 只看应用层日志:启动阶段的问题必须回到串口与
dmesg,用户空间日志往往已经太晚。