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 选项之间的依赖与选择关系:
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 编译流程架构图
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 来修复依赖关系。否则,缺失的依赖会导致编译失败或运行时异常。
典型裁剪流程:
- 备份:
cp .config .config.bak - 禁用驱动:将
CONFIG_XXX=y改为# CONFIG_XXX is not set - 模块化:将
CONFIG_XXX=y改为CONFIG_XXX=m(如果支持) - 修复依赖:
make olddefconfig - 验证尺寸:
make zImage后检查vmlinux或zImage大小
3.3.4 .config 与 Kconfig 的映射关系
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 验证编译通过。