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 的作用方向:

Kconfig 依赖关系示例 CONFIG_NET CONFIG_PACKET CONFIG_UNIX CONFIG_INET CONFIG_TCP 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

编译流程:

  1. 顶层Makefile读取 .config 文件,生成所有 CONFIG_* 变量。
  2. 递归进入各子目录,Kbuild根据 obj-$(CONFIG_XXX) 的值决定是否编译该目录或文件。
  3. 如果 CONFIG_E1000=y,则 obj-y 包含 e1000/,该目录下的所有文件被编译并链接进内核。
  4. 如果 CONFIG_E1000=m,则 obj-m 包含 e1000/,编译生成 e1000.ko 模块。
裁剪实战:在裁剪时,你只需要关注 obj-$(CONFIG_XXX) 行。如果某个驱动被编译进了内核(=y),但你的硬件不需要它,就在menuconfig中将其设为 n,对应的 obj-y 行就会变为空,该驱动不会被编译。

4.2.3 Kbuild编译流程SVG图

下图展示了从 .config 到最终内核镜像的编译数据流:

Kbuild 编译数据流 .config 文件 顶层 Makefile obj-y:编译进内核 obj-m:编译为模块 vmlinux(内核镜像) *.ko(可加载模块)

4.3 配置选项的层次结构

Linux内核的配置选项并非扁平排列,而是具有清晰的树形层次结构。理解这个层次,能让你在menuconfig中快速定位选项,避免遗漏依赖。

4.3.1 层次结构模型

配置层次从上到下分为四层:

  1. 顶层菜单:如 General setup、Device Drivers、File systems、Networking support。
  2. 功能子菜单:如 Device Drivers → Network device support → Ethernet driver support。
  3. 驱动/功能组:如 Ethernet driver support → Intel devices。
  4. 具体配置项:如 Intel(R) PRO/1000 Gigabit Ethernet support(即 CONFIG_E1000)。

4.3.2 层次结构SVG图

下图展示了menuconfig中典型的树形菜单结构:

内核配置选项层次结构 Top Level Menu General setup Device Drivers File systems ... Network device support Ethernet driver support CONFIG_E1000 CONFIG_E1000E CONFIG_TIGON3

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)、查看依赖(?) 使用搜索快速定位选项,使用 ? 查看完整依赖