4、内核配置系统入门:Kconfig语法基础、Makefile与内核编译系统、配置选项的层次结构
嵌入式Linux内核裁剪的第一步,是理解内核的配置系统。Linux内核使用了一套高度模块化的配置框架,由Kconfig(定义配置选项)、Kbuild/Makefile(定义编译规则)和配置工具(如menuconfig)三部分组成。本章将深入剖析这三者的协作机制,帮助你在裁剪时精准定位并修改配置项。
4.1 Kconfig语法基础
Kconfig文件是内核配置的“词典”,它定义了所有可配置选项的名称、类型、依赖关系和帮助信息。每个内核子目录(如 drivers/net/)下都有一个 Kconfig 文件。
4.1.1 核心语法元素
| 语法元素 | 关键字/格式 | 说明 |
|---|---|---|
| 配置项 | config CONFIG_XXX |
定义一个布尔、三态或字符串类型的配置选项 |
| 菜单 | menu "文字" … endmenu |
在menuconfig中创建折叠菜单 |
| 选择 | choice … endchoice |
定义一组互斥选项(如CPU架构选择) |
| 依赖 | depends on |
当前选项仅在依赖条件满足时可见 |
| 反向依赖 | select |
选中当前选项时,自动选中另一个选项 |
| 默认值 | default y / default n |
设置选项的默认状态 |
| 提示 | prompt "显示文字" |
配置界面中显示的文本 |
| 帮助 | help 或 ---help--- |
选项的详细说明文档 |
4.1.2 典型Kconfig片段解析
menu "Networking support"
config NET
bool "Networking support"
default y
help
Say Y here if you want networking support.
config PACKET
tristate "Packet socket"
depends on NET
default y
help
The Packet socket protocol is used by tcpdump and other tools.
config UNIX
tristate "Unix domain sockets"
depends on NET
default y
endmenu
关键点解读:
- bool:选项只有Y/N两种状态,编译进内核。
- tristate:选项有三种状态:Y(编译进内核)、N(不编译)、M(编译为可加载模块)。
- depends on NET:只有当
CONFIG_NET=y时,PACKET和UNIX才会出现在配置界面中。
bool 而非 tristate,将功能直接编译进内核,避免模块加载带来的文件系统和启动复杂度。除非该驱动需要动态加载或卸载。
4.1.3 Kconfig依赖关系SVG图
下图展示了一个典型Kconfig配置项的依赖链,以及 depends on 和 select 的作用方向:
4.2 Makefile与内核编译系统
Kconfig定义了“编译什么”,而Makefile(Kbuild)定义了“怎么编译”。内核的编译系统称为Kbuild,它使用递归Makefile结构,每个子目录的Makefile负责将该目录下的源文件编译成目标文件或模块。
4.2.1 核心Makefile变量
| 变量名 | 作用 | 示例 |
|---|---|---|
obj-y |
无条件编译进内核的目标文件 | obj-y += mydriver.o |
obj-m |
编译为可加载模块的目标文件 | obj-m += mymodule.o |
obj-$(CONFIG_XXX) |
根据配置选项决定是否编译 | obj-$(CONFIG_MYDRV) += mydriver.o |
ccflags-y |
传递给C编译器的额外标志 | ccflags-y += -DDEBUG |
ldflags-y |
传递给链接器的额外标志 | ldflags-y += -T mylink.lds |
4.2.2 典型驱动Makefile示例
# drivers/net/ethernet/Makefile
#
# Makefile for the Ethernet network device drivers.
obj-y += 8390/
obj-y += amd/
obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_E1000E) += e1000e/
# 单个文件编译
obj-$(CONFIG_MY_CUSTOM_ETH) += my_eth_driver.o
my_eth_driver-objs := my_eth_main.o my_eth_phy.o my_eth_dma.o
编译流程:
- 顶层Makefile读取
.config文件,生成所有CONFIG_*变量。 - 递归进入各子目录,Kbuild根据
obj-$(CONFIG_XXX)的值决定是否编译该目录或文件。 - 如果
CONFIG_E1000=y,则obj-y包含e1000/,该目录下的所有文件被编译并链接进内核。 - 如果
CONFIG_E1000=m,则obj-m包含e1000/,编译生成e1000.ko模块。
obj-$(CONFIG_XXX) 行。如果某个驱动被编译进了内核(=y),但你的硬件不需要它,就在menuconfig中将其设为 n,对应的 obj-y 行就会变为空,该驱动不会被编译。
4.2.3 Kbuild编译流程SVG图
下图展示了从 .config 到最终内核镜像的编译数据流:
4.3 配置选项的层次结构
Linux内核的配置选项并非扁平排列,而是具有清晰的树形层次结构。理解这个层次,能让你在menuconfig中快速定位选项,避免遗漏依赖。
4.3.1 层次结构模型
配置层次从上到下分为四层:
- 顶层菜单:如
General setup、Device Drivers、File systems、Networking support。 - 功能子菜单:如
Device Drivers → Network device support → Ethernet driver support。 - 驱动/功能组:如
Ethernet driver support → Intel devices。 - 具体配置项:如
Intel(R) PRO/1000 Gigabit Ethernet support(即CONFIG_E1000)。
4.3.2 层次结构SVG图
下图展示了menuconfig中典型的树形菜单结构:
4.3.3 层次结构对裁剪的影响
Device Drivers → Network device support,但你的应用需要socket通信,这会导致内核编译失败或功能缺失。正确的做法是:先确定硬件需求,然后沿着层次树逐级向下裁剪,确保每个父级选项都被正确启用。
4.4 配置工具的使用
内核提供了多种配置界面,最常用的是基于ncurses的 menuconfig:
# 进入内核源码根目录
make menuconfig
# 常用操作键
# ↑/↓ : 移动光标
# Enter : 进入子菜单 / 选中
# Space : 切换选项状态(Y/N/M)
# / : 搜索配置项(支持模糊搜索)
# ? : 查看选项的帮助和依赖
# Esc+Esc : 返回上一级 / 退出
搜索功能实战:
当你需要裁剪某个特定驱动时,按 / 键输入关键词(如 e1000),menuconfig会列出所有匹配的配置项及其路径:
Symbol: E1000 [=y]
Type : tristate
Prompt: Intel(R) PRO/1000 Gigabit Ethernet support
Location:
-> Device Drivers
-> Network device support (NETDEVICES [=y])
-> Ethernet driver support (ETHERNET [=y])
-> Intel devices
Defined at drivers/net/ethernet/intel/e1000/Kconfig:5
Depends on: NETDEVICES [=y] && E1000 [=y]
这个输出直接告诉你:CONFIG_E1000 位于 Device Drivers → Network device support → Ethernet driver support → Intel devices 下,并且依赖 NETDEVICES 和 ETHERNET。裁剪时,你可以沿着这个路径逐级检查。
4.5 本章小结
| 知识点 | 核心要点 | 裁剪应用 |
|---|---|---|
| Kconfig语法 | config、menu、choice、depends on、select |
通过 depends on 追踪依赖链,避免裁剪后编译失败 |
| Makefile/Kbuild | obj-y、obj-m、obj-$(CONFIG_XXX) |
修改 .config 后,Kbuild自动决定编译哪些文件 |
| 层次结构 | 顶层菜单 → 子菜单 → 功能组 → 具体选项 | 从顶层开始逐级裁剪,确保父级选项满足依赖 |
| menuconfig工具 | 搜索(/)、切换状态(Space)、查看依赖(?) | 使用搜索快速定位选项,使用 ? 查看完整依赖 |