1. 课程导入

从 Android 5.0 引入 Treble 计划开始,Android 的底层软件架构经历了一次持续十余年的解耦演进。到 Android 11 正式落地 GKI(Generic Kernel Image,通用内核镜像),内核与厂商驱动的边界被重新划定:Google 负责统一内核镜像与 KMI(Kernel Module Interface)稳定性,芯片厂商与 OEM 则通过可加载模块和供应商分区完成差异化适配。这一变化直接影响了设备树(Device Tree)的组织方式与启动链路的适配策略,也是本课程要解决的核心问题。

本课程的目标不是让你背下某个平台的启动日志,而是建立一套可迁移的方法:看懂 GKI 的边界划分,理解设备树在启动链路中的位置,掌握从 bootloader 到 Android 用户空间的适配与排障路径。

一、为什么会有 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 用户空间的关键链路,以及设备树与各分区的对应关系。

GKI 启动链路与设备树分布 Bootloader 解析分区表 加载 boot / vendor_boot GKI 内核启动 解压内核、初始化核心子系统 建立 KMI 符号表 设备树解析 DTB 叠加 / overlay 合并 生成 /proc/device-tree 模块加载 vendor_dlkm 中 .ko 驱动 probe 匹配 设备树与模块的分区来源 boot 分区 GKI 内核(Image.lz4) 通用 ramdisk 基础 DTB(可选) vendor_boot 分区 供应商 ramdisk DTB / DTBO(板级差异) vendor_boot.modules vendor_dlkm 分区 供应商驱动模块 .ko modules.dep / load 与 KMI 版本绑定 适配关注点 ① 设备树节点与驱动 compatible 是否匹配 ② DTB/DTBO 叠加顺序与 overlay 冲突 ③ 模块依赖与加载时机(first-stage / second-stage) ④ KMI 符号缺失导致模块加载失败 ⑤ 启动参数与 cmdline 传递 ⑥ 分区大小与 AVB 校验约束

可以看到,设备树不再是"内核源码里的一个 dts 文件",而是横跨 boot、vendor_boot 两个分区、在启动早期被合并的运行时数据。适配工作的本质,就是让这条链路上的每一环都能正确衔接。

三、整体知识框架与实战路线

本课程按"认知 → 结构 → 适配 → 排障 → 复盘"的顺序展开,共 30 章,可归纳为五个模块:

模块核心内容典型产出
背景与架构GKI、KMI、Treble、分区布局能画出启动链路与分区关系
设备树基础DTS/DTB/DTBO、overlay、compatible能读懂并修改板级设备树
启动适配boot/vendor_boot 构建、模块加载、cmdline能完成一次完整启动适配
调试与排障串口日志、dmesg、模块加载失败分析能定位启动卡死与驱动 probe 失败
工程化与复盘版本管理、CI 校验、升级兼容能建立可维护的适配流程

实战路线建议按以下顺序推进,每一步都要有可验证的结果:

  1. 搭建编译环境,能独立编译出 GKI 内核与 vendor 模块。
  2. 解包目标设备的 boot 与 vendor_boot,确认设备树与模块的实际内容。
  3. 修改一处设备树节点(如新增一个 I2C 设备),验证驱动能否 probe 成功。
  4. 制造一次典型故障(如删除模块依赖),用日志定位并修复。
  5. 整理成可复用的适配清单与检查脚本。

四、典型场景与常见误区

以一个真实场景为例:某设备升级到 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,用户空间日志往往已经太晚。
排障时不要急于修改代码。先确认"设备树是否被正确加载、模块是否被正确加载、compatible 是否匹配"这三件事,能解决大部分启动适配问题。
建议在课程学习过程中同步准备一台可刷机的开发设备或模拟环境,每学完一个模块就动手验证一次。GKI 适配是实践性很强的技能,只看不练很难形成稳定的判断力。