3. KMI核心概念解析

KMI(Kernel Module Interface,内核模块接口)是Android GKI(Generic Kernel Image)架构下,用于隔离“通用内核”与“厂商模块”之间依赖关系的一组稳定接口契约。理解KMI,不能只把它当作几个导出函数,而要从符号、接口、版本、稳定性四个维度建立整体认知:符号是KMI的最小可见单元,接口是符号按功能组织后的能力集合,版本是接口随时间演进的坐标,稳定性则是Android生态对“接口不可随意破坏”的工程承诺。

一、KMI符号:接口的最小可见单元

KMI符号是内核向模块暴露的、可被解析和引用的命名实体,通常表现为EXPORT_SYMBOL或EXPORT_SYMBOL_GPL导出的函数、变量,以及模块加载时依赖的__ksymtab条目。在GKI模式下,厂商模块只能使用KMI白名单中的符号,不能依赖未导出的内部实现。符号的“可见性”直接决定了模块能否编译、加载和运行。

从工程视角看,KMI符号分为三类:

  • 稳定符号:已纳入KMI清单,承诺在ABI版本生命周期内保持签名与语义不变。
  • 受控符号:允许使用,但需关注版本变化,升级时可能被替换或废弃。
  • 内部符号:未导出或仅GPL内部使用,厂商模块不得依赖,否则会在GKI内核上加载失败。

例如,模块中常见的module_init、register_chrdev、class_create等,若出现在KMI清单中,则属于稳定符号;而某些SoC专用时钟、电源域操作函数,往往不在KMI内,需要厂商以模块方式自行实现或通过其他稳定接口间接完成。

二、KMI接口:符号按能力组织的契约

KMI接口不是单个符号,而是围绕某一类内核能力组织起来的符号集合。例如,字符设备接口、sysfs接口、DMA映射接口、时钟框架接口、电源管理接口等。每个接口都有明确的调用约定、参数类型、返回值语义和生命周期要求。厂商模块通过调用这些接口,完成驱动注册、资源申请、中断处理、数据收发等操作。

KMI接口的核心价值在于“解耦”:GKI内核负责提供稳定、通用的实现,厂商模块负责硬件相关逻辑。这样,Google可以独立升级GKI内核,而厂商模块无需随内核每次变动重新编译。接口的边界越清晰,模块与内核的耦合度越低,升级成本越小。

关键理解:KMI符号是“点”,KMI接口是“面”。一个接口可能由多个符号组成,一个符号也可能被多个接口复用。判断兼容性时,既要看单个符号是否存在,也要看接口整体语义是否保持一致。

三、KMI版本:接口演进的坐标

KMI版本用于标识一组KMI接口在某一时间点的快照。Android GKI通过kmi_abi_version或类似机制记录接口集合的版本号。版本号通常与内核版本、ABI描述文件、符号清单绑定。厂商模块在编译时,会针对特定KMI版本生成依赖信息;加载时,内核会校验模块所需的KMI版本是否与当前内核匹配。

版本演进遵循“向后兼容优先”的原则:新增符号通常不破坏旧模块;删除或修改符号则必须提升主版本号,并给出迁移窗口。实际工程中,KMI版本常以android12-5.10、android13-5.15、android14-6.1等形式出现,分别对应Android版本与内核版本组合。

# 查看当前内核KMI版本与符号信息(示例)
adb shell cat /proc/version
adb shell cat /sys/kernel/kmi/abi_version
adb shell grep -i "kmi" /proc/kallsyms | head -20

# 模块加载时的典型版本校验日志
[  12.345678] my_module: loading out-of-tree module taints kernel
[  12.345679] my_module: KMI version mismatch: module requires android13-5.15, kernel provides android14-6.1
[  12.345680] my_module: module verification failed: signature and/or required key missing

四、KMI稳定性要求:Android生态的工程承诺

KMI稳定性要求是Android生态对厂商、芯片厂和OEM的共同约束。其核心目标是:在同一个GKI内核分支内,KMI接口的ABI保持稳定,厂商模块无需重新编译即可跨小版本升级。稳定性要求通常包括:

  • 符号签名稳定:函数参数类型、数量、返回值、调用约定不变。
  • 数据结构布局稳定:结构体字段偏移、大小、对齐方式不变。
  • 语义稳定:接口行为、错误码、生命周期规则不变。
  • 版本可追溯:每次变更都有记录,废弃接口有过渡期。

如果稳定性被破坏,后果不仅是模块加载失败,还可能引发内核崩溃、数据损坏、安全漏洞。因此,KMI变更必须经过严格评审,并通过ABI对比工具(如abidiff、libabigail)检测。

五、KMI在Android生态中的角色

在Android生态中,KMI扮演“中间契约层”的角色。向上,它支撑Android Framework、HAL、Vendor模块的稳定运行;向下,它约束GKI内核的导出行为。没有KMI,每个厂商都要维护自己的内核分支,导致碎片化严重、安全补丁难以统一、升级周期漫长。有了KMI,Google可以统一发布GKI内核,厂商只需维护符合KMI的模块,从而缩短升级链路、提升安全响应速度。

维度 KMI符号 KMI接口 KMI版本 KMI稳定性
关注对象 单个导出函数/变量 一类能力的符号集合 接口快照的编号 ABI不变性承诺
典型载体 __ksymtab、EXPORT_SYMBOL 字符设备、DMA、时钟框架 android13-5.15 ABI描述文件、评审流程
失败表现 符号未定义、加载失败 接口行为异常、崩溃 版本不匹配、拒绝加载 模块无法跨版本复用
工程动作 检查符号白名单 按接口规范调用 对齐目标KMI版本 ABI对比与兼容性测试

六、案例与常见误区

某厂商在Android 13 GKI内核上开发Wi-Fi模块,直接引用了未导出的cfg80211_internal_ops符号。编译时通过,但加载时报Unknown symbol。复盘发现,该符号属于内部实现,不在KMI清单中。正确做法是改用KMI稳定接口cfg80211_register_ops,或通过HAL层转发。另一个常见误区是“只要内核版本相同,KMI就兼容”。实际上,同一内核版本的不同GKI分支可能包含不同的KMI快照,必须核对abi_version。

排障提示:遇到模块加载失败,优先检查三件事:符号是否在KMI白名单、KMI版本是否匹配、模块签名与内核是否一致。不要盲目修改内核配置或关闭校验,这会破坏GKI的稳定性承诺。

总结来看,KMI符号是接口的原子,KMI接口是能力的组织,KMI版本是演进的坐标,KMI稳定性是生态的底线。四者共同构成Android GKI时代内核与厂商模块之间的“稳定契约”,也是ABI兼容工程的核心抓手。

KMI核心概念与Android生态角色 符号 → 接口 → 版本 → 稳定性:GKI与厂商模块之间的稳定契约 GKI 通用内核 提供稳定实现 导出KMI符号 维护ABI版本 KMI 稳定契约层 KMI符号 · KMI接口 KMI版本 · 稳定性要求 ABI白名单与校验 厂商模块 驱动 / HAL / Vendor 仅依赖KMI符号 按KMI版本编译 导出 调用 1 KMI符号 EXPORT_SYMBOL导出 __ksymtab可见性 最小可见单元 2 KMI接口 字符设备 / DMA / 时钟 按能力组织的符号集 调用约定与语义 3 KMI版本 android13-5.15 接口快照编号 加载时版本校验 4 稳定性要求 签名 / 布局 / 语义不变 ABI对比与评审 生态升级底线 Android 生态角色:统一GKI内核 · 降低碎片化 · 加速安全补丁 · 缩短升级链路 Framework / HAL / Vendor 模块通过KMI稳定契约与GKI内核协同工作