3、内核配置系统基础:Kconfig语法入门、Makefile与内核编译流程、.config文件结构解析

嵌入式Linux内核裁剪的第一步,是理解内核的配置系统。内核配置系统由三大部分组成:Kconfig(配置选项描述语言)、Makefile(编译规则)和.config(配置结果文件)。这三者协同工作,构成了从“选择功能”到“生成镜像”的完整链路。

核心认知: Kconfig 定义“有什么选项”,.config 记录“选了哪些”,Makefile 决定“怎么编译”。裁剪内核的本质就是精确操控 .config 中的每一个符号。

3.1 Kconfig 语法入门

Kconfig 是内核专用的配置描述语言,用于定义配置选项的层次结构、依赖关系和默认值。所有 Kconfig 文件分布在各个子目录中,通过 source 语句串联成树状结构。

3.1.1 基本语法元素

语法元素 作用 示例
config 定义一个配置选项,生成 CONFIG_XXX 宏 config MY_FEATURE
bool / tristate 选项类型:布尔(y/n)或三态(y/m/n) bool "Enable My Feature"
depends on 设置依赖关系,前置选项必须为 y depends on ARM
select 反向依赖,选中本选项时强制选中另一选项 select LIBFDT
default 设置默认值 default y if ARCH_OMAP2
help 帮助文本,描述选项用途 help\n This enables ...
menu / endmenu 创建菜单分组,用于界面展示 menu "Networking support"
choice / endchoice 单选组,只能从多个选项中选一个 choice\n prompt "CPU type"

3.1.2 完整 Kconfig 示例

# drivers/my_driver/Kconfig
menu "My Custom Drivers"

config MY_DRIVER_CORE
    bool "My Driver Core Support"
    default y
    help
      Enable core support for My Driver. Required for all sub-features.

config MY_DRIVER_DEBUG
    bool "My Driver Debug"
    depends on MY_DRIVER_CORE
    default n
    help
      Enable debug output for My Driver. Increases kernel size.

config MY_DRIVER_DMA
    bool "My Driver DMA Engine"
    depends on MY_DRIVER_CORE
    select DMA_ENGINE
    help
      Enable DMA support. Automatically selects the DMA_ENGINE subsystem.

endmenu
裁剪技巧: 在裁剪时,重点关注 select 语句——它可能在你启用一个选项时,悄悄拉入大量依赖代码。使用 make menuconfig 的搜索功能(/键)可以快速定位这些反向依赖。

3.1.3 Kconfig 依赖关系可视化

下图展示了一个典型 Kconfig 选项之间的依赖与选择关系:

Kconfig 依赖关系示例 CONFIG_MY_DRIVER_CORE CONFIG_MY_DRIVER_DEBUG CONFIG_MY_DRIVER_DMA CONFIG_DMA_ENGINE depends on depends on select 顶层选项 依赖选项 主动选项 被选中项

3.2 Makefile 与内核编译流程

内核的 Makefile 体系采用递归分层结构,从顶层 Makefile 开始,逐级进入子目录。Kconfig 生成的 CONFIG_* 宏直接控制 Makefile 中的编译目标。

3.2.1 条件编译模式

# drivers/my_driver/Makefile
obj-$(CONFIG_MY_DRIVER_CORE)    += my_driver_core.o
obj-$(CONFIG_MY_DRIVER_DEBUG)   += my_driver_debug.o
obj-$(CONFIG_MY_DRIVER_DMA)     += my_driver_dma.o

当 CONFIG_MY_DRIVER_CORE=y 时,obj-y 会将 my_driver_core.o 编译进内核;当 CONFIG_MY_DRIVER_DEBUG=m 时,obj-m 会将其编译为独立模块。

3.2.2 内核编译完整流程

步骤 命令 说明
1. 配置 make menuconfig 基于 Kconfig 生成 .config 文件
2. 生成依赖 make olddefconfig 自动补全未定义选项为默认值
3. 编译内核 make zImage / make uImage 编译生成内核镜像
4. 编译模块 make modules 编译所有标记为 =m 的模块
5. 安装模块 make modules_install 将模块安装到 /lib/modules/

3.2.3 编译流程架构图

内核编译流程架构 Kconfig 文件 Makefile 文件 源代码 (*.c, *.h) make menuconfig .config 文件 zImage / modules

3.3 .config 文件结构解析

.config 是内核配置的最终输出文件,位于内核源码根目录。它是一个纯文本文件,每一行对应一个配置选项的值。裁剪内核时,直接编辑 .config 是最快速的方法,但必须谨慎处理依赖关系。

3.3.1 .config 文件格式

# 注释行:以 # 开头
# ARM architecture

# 布尔选项:y 表示编译进内核
CONFIG_ARM=y

# 三态选项:m 表示编译为模块
CONFIG_SOUND=m

# 整型选项
CONFIG_NR_CPUS=4

# 字符串选项
CONFIG_DEFAULT_HOSTNAME="(none)"

# 未设置选项:被注释掉,值为 is not set
# CONFIG_USB_NET_DRIVERS is not set

3.3.2 关键字段说明

符号 含义 对尺寸的影响
=y 编译进内核镜像 直接增加内核镜像体积
=m 编译为可加载模块 不增加内核镜像体积,但占用文件系统空间
is not set 完全禁用 不产生任何代码,最优裁剪
=<number> 数值参数 影响数据结构大小(如 NR_CPUS)
="string" 字符串参数 影响默认参数和运行时行为

3.3.3 裁剪实战:从 .config 入手

警告: 直接编辑 .config 后,必须运行 make olddefconfig 来修复依赖关系。否则,缺失的依赖会导致编译失败或运行时异常。

典型裁剪流程:

  1. 备份:cp .config .config.bak
  2. 禁用驱动:将 CONFIG_XXX=y 改为 # CONFIG_XXX is not set
  3. 模块化:将 CONFIG_XXX=y 改为 CONFIG_XXX=m(如果支持)
  4. 修复依赖:make olddefconfig
  5. 验证尺寸:make zImage 后检查 vmlinux 或 zImage 大小

3.3.4 .config 与 Kconfig 的映射关系

Kconfig → .config → Makefile 映射 Kconfig 定义 config MY_DRIVER bool "My Driver" default y depends on ARM select LIBFDT .config 结果 CONFIG_ARM=y CONFIG_MY_DRIVER=y CONFIG_LIBFDT=y # CONFIG_OTHER is not set Makefile 编译 obj-$(CONFIG_MY_DRIVER) += my_driver.o obj-$(CONFIG_LIBFDT) += libfdt.o

3.4 配置系统交互流程总结

整个配置系统的数据流可以概括为:

  • Kconfig 定义选项树 → 配置工具(menuconfig/xconfig)生成用户界面 → 用户选择 → 输出 .config
  • .config 被顶层 Makefile 读取 → 生成 include/generated/autoconf.h(C 头文件)→ 源代码通过 #ifdef CONFIG_XXX 条件编译
  • Makefile 通过 obj-$(CONFIG_XXX) 决定哪些文件参与编译 → 最终链接为 vmlinux → 压缩为 zImage
裁剪切入点: 对于嵌入式系统,建议先使用 make defconfig 生成默认配置,然后通过 make menuconfig 逐项裁剪。对于批量裁剪,可以编写脚本直接操作 .config 文件,但务必在修改后执行 make olddefconfig 和 make 验证编译通过。