2. 内核配置基础回顾:Kconfig语法、Makefile与.config文件结构

在深入设备树驱动的模块化裁剪之前,必须牢固掌握内核配置系统的三大支柱:Kconfig(配置选项定义)、Makefile(编译规则)和 .config(配置结果存储)。这三者构成了从“配置意图”到“二进制产物”的完整链路。

2.1 三者的角色与数据流

内核配置系统的工作流程可以概括为:Kconfig 定义“有什么可配”,用户通过 make menuconfig 等工具做出选择,结果写入 .config,Makefile 根据 .config 中的变量决定编译哪些源文件。下图展示了这一数据流:

内核配置数据流 Kconfig 文件 定义配置选项树 menuconfig / 工具 用户交互选择 .config 文件 存储配置结果 Makefile 条件编译规则 关键路径 Kconfig → 工具 → .config .config → Makefile → 编译
要点:设备树的模块化裁剪本质上是在 Kconfig 层面对驱动选项进行“拆分”和“条件化”,然后在 Makefile 中利用 CONFIG_* 变量控制设备树源文件(.dts)的编译与包含。

2.2 Kconfig 语法核心

Kconfig 文件位于每个驱动目录下(如 drivers/i2c/Kconfig),使用一种类脚本语言定义配置选项。以下是驱动开发中最常用的语法元素:

语法元素 作用 示例
config 定义一个配置选项,生成 CONFIG_* 变量 config SENSOR_TMP117
tristate 三态选项:n(不编译)、y(编译进内核)、m(编译为模块) tristate "TMP117 temperature sensor"
bool 布尔选项:只能选 y 或 n bool "Enable debug output"
depends on 依赖关系:只有前置选项满足时才可见 depends on I2C
select 反向选择:选中本选项时自动选中另一选项 select REGMAP_I2C
help 帮助文本,在 menuconfig 中显示 help
  Say Y here to support...
if / endif 条件块:批量控制多个选项的可见性 if EXPERT ... endif
menu / endmenu 创建子菜单,组织选项层次 menu "Sensor drivers" ... endmenu
模块化裁剪关键:使用 tristate 而非 bool 是驱动模块化的前提。只有 tristate 选项才能被编译为 .ko 文件,从而实现运行时按需加载。

一个典型的驱动 Kconfig 条目示例:

config SENSOR_TMP117
    tristate "TI TMP117 temperature sensor"
    depends on I2C
    select REGMAP_I2C
    help
      Support for the TI TMP117 high-accuracy temperature sensor.
      This driver can be built as a module. If unsure, say N.

      If compiled as a module, it will be called tmp117.

2.3 Makefile 中的条件编译

内核 Makefile 使用 obj-$(CONFIG_*) 模式实现条件编译。当 CONFIG_* 为 y 时,目标文件编译进内核;为 m 时编译为模块;为 n 或不定义时不编译。

.config 中的值 Makefile 展开结果 编译行为
CONFIG_SENSOR_TMP117=y obj-y += tmp117.o 编译进内核镜像
CONFIG_SENSOR_TMP117=m obj-m += tmp117.o 编译为可加载模块
# CONFIG_SENSOR_TMP117 is not set obj-$(CONFIG_SENSOR_TMP117) 展开为空 不编译

驱动目录下的 Makefile 通常非常简洁:

# SPDX-License-Identifier: GPL-2.0
#
# Makefile for I2C sensor drivers
#

obj-$(CONFIG_SENSOR_TMP117)    += tmp117.o
obj-$(CONFIG_SENSOR_BME280)    += bme280.o
obj-$(CONFIG_SENSOR_OPT3001)   += opt3001.o
常见错误:初学者容易在 Makefile 中直接写 obj-y += tmp117.o,这会导致驱动始终编译进内核,无法模块化。必须使用 obj-$(CONFIG_*) 形式。

2.4 .config 文件结构

.config 是内核配置的“快照”文件,位于内核源码根目录。它由 make *config 系列工具自动生成,也是 Makefile 读取配置变量的来源。其格式为简单的键值对:

#
# Automatically generated file; DO NOT EDIT.
# Linux/arm64 6.1.0 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="aarch64-linux-gnu-gcc (GCC) 12.2.0"
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=120200

# 三态选项的三种状态
CONFIG_SENSOR_TMP117=m      # 编译为模块
CONFIG_SENSOR_BME280=y      # 编译进内核
# CONFIG_SENSOR_OPT3001 is not set  # 不编译

# 布尔选项
CONFIG_DEBUG_FS=y
# CONFIG_DEBUG_KERNEL is not set

.config 文件的关键特征:

  • 自动生成:不要手动编辑 .config,应通过 make menuconfig 等工具修改。
  • 三态表示:=y(编译进内核)、=m(模块)、is not set(不编译)。
  • 依赖自动处理:当选择 CONFIG_SENSOR_TMP117=m 时,其依赖的 CONFIG_REGMAP_I2C 会被自动设为 =y 或 =m。
  • 顶层包含:arch/<arch>/configs/ 下的 defconfig 是 .config 的模板,用于快速生成初始配置。

2.5 模块化裁剪的配置策略

基于上述基础,驱动模块化裁剪的配置策略可以归纳为以下步骤:

  1. 识别驱动选项:在 Kconfig 中确认目标驱动是否为 tristate,若不是则需修改。
  2. 拆分依赖链:将驱动依赖的通用层(如 regmap、IIO 核心)也设为 tristate,避免强制编译进内核。
  3. 设置模块化:在 .config 中将目标驱动设为 =m,其依赖自动跟随。
  4. 验证 Makefile:确保驱动目录的 Makefile 使用 obj-$(CONFIG_*) 而非 obj-y。
  5. 编译与测试:使用 make modules 编译模块,insmod/modprobe 加载测试。
模块化裁剪配置流程 1. 识别 tristate 检查 Kconfig 2. 拆分依赖 设为 tristate 3. 配置 =m menuconfig 设置 4. 验证 Makefile obj-$(CONFIG_*) 5. 编译测试 make modules 核心原则 只有 tristate 选项才能模块化;依赖链上的所有选项都必须支持 =m
实践建议:在开始设备树驱动裁剪前,先对目标驱动执行 make menuconfig 搜索其选项名(按 / 键),确认其类型和依赖关系。这是避免“配置了但编译不进去”的最有效方法。

2.6 本章小结