第四章 内核配置系统详解:Kconfig语法、Makefile与内核编译系统、defconfig与menuconfig的使用
说实话,内核配置系统这东西,刚入行那会儿我觉得挺简单的。不就是 make menuconfig 嘛,谁不会呢?
但后来我在一个Exynos 4412的项目上栽了个跟头——因为不懂Kconfig的依赖关系,配出来的内核死活编译不过。嗯,从那以后我就老老实实把整个配置系统啃了一遍。
今天咱们就把这块彻底讲透。你想想看,内核里几千个驱动,怎么管理它们开还是关?怎么保证你选的配置不会互相打架?答案就在Kconfig和Makefile里。
4.1 Kconfig语法:内核配置的“宪法”
Kconfig文件定义了所有配置选项。每个驱动、每个子系统,都得在这里“注册”一下。
我习惯把Kconfig比作一张菜单。菜单上写着:有宫保鸡丁(CONFIG_DRIVER_A)、有鱼香肉丝(CONFIG_DRIVER_B)。你勾哪个,编译系统就做哪个菜。
来看一个典型的Kconfig条目:
config EXYNOS_MMC
tristate "Exynos MMC/SD controller support"
depends on ARM
select MMC_BLOCK
help
This driver supports the MMC/SD controller found on
Samsung Exynos SoCs.
If unsure, say N.
这里有几个关键字段:
- config:定义选项名,最终会变成
CONFIG_EXYNOS_MMC - tristate:三态——Y(编入内核)、M(模块)、N(不选)。如果是
bool,就只有Y和N - depends on:依赖关系。比如这个驱动只能在ARM架构下选
- select:反向依赖。选了它,自动选上
MMC_BLOCK - help:帮助信息,menuconfig里按?就能看到
重要提醒:select 和 depends on 的区别一定要搞清楚。depends on 是“我依赖别人”,select 是“别人依赖我”。用反了,配置系统会出逻辑矛盾。
再看一个带 choice 的例子:
choice
prompt "Exynos CPU frequency governor"
default CPU_FREQ_GOV_PERFORMANCE
config CPU_FREQ_GOV_PERFORMANCE
bool "Performance"
config CPU_FREQ_GOV_POWERSAVE
bool "Powersave"
config CPU_FREQ_GOV_ONDEMAND
bool "Ondemand"
endchoice
choice就是单选。你只能选一个CPU调频策略。我在Exynos 5422的项目里,就因为这个选错了,导致板子功耗一直下不来。后来改成ondemand,温度直接降了10度。
4.2 Makefile与内核编译系统
Kconfig定义“能不能选”,Makefile决定“怎么编译”。两者配合,天衣无缝。
内核的Makefile体系分三层:
- 顶层Makefile:负责整个内核的构建流程
- 架构Makefile:arch/arm/Makefile,处理架构相关的东西
- 子目录Makefile:每个驱动目录下都有,比如 drivers/mmc/Makefile
子目录Makefile的写法很简单:
# drivers/mmc/Makefile
obj-$(CONFIG_EXYNOS_MMC) += exynos_mmc.o
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
obj-m += some_module.o
这里 obj-y 表示编入内核,obj-m 表示编译成模块,obj-n 就是不编译。
我刚开始看这个的时候,觉得不就是个条件编译嘛。但后来发现,内核的Makefile远不止这么简单。它还有:
- ccflags-y:给这个目录下的文件加编译参数
- subdir-ccflags-y:递归给子目录加参数
- lib-y:编译成库文件
我的小技巧:调试驱动时,可以在Makefile里加一行 ccflags-y += -DDEBUG,这样就能打开驱动的调试打印。不用改代码,方便得很。
4.3 defconfig与menuconfig:两种配置方式
配置内核有两种主流方式:批量的defconfig,和交互的menuconfig。
4.3.1 defconfig——批量配置
每个架构、每个板子都有自己的默认配置。Exynos的配置在 arch/arm/configs/ 下:
$ ls arch/arm/configs/ | grep exynos
exynos_defconfig
exynos4210_defconfig
exynos4412_defconfig
用法很简单:
$ make exynos_defconfig
$ make -j4
defconfig文件里只保存了非默认值的选项。比如默认 CONFIG_MMC=y,那defconfig里就不会写它。只有你改了的值才会被记录。
注意:defconfig不是完整的.config文件。它只记录“差异”。所以如果你手动改了.config,然后 make exynos_defconfig,你的修改会被覆盖掉。我曾经因为这个丢过一上午的工作量……
4.3.2 menuconfig——交互配置
menuconfig是基于ncurses的图形界面。运行:
$ make menuconfig
你会看到一个分层的菜单树。上下键移动,空格键切换选择,回车进入子菜单。
我个人习惯的流程是:
- 先用
make exynos_defconfig加载默认配置 - 再用
make menuconfig微调 - 最后
make savedefconfig生成精简的defconfig
为什么要这样?因为默认配置通常能启动,但有很多用不上的驱动。裁剪内核,就是在menuconfig里一个个关掉不需要的东西。
举个例子,Exynos 4412的开发板,默认配置会打开HDMI驱动。但如果你只用LCD屏,就可以关掉它:
Device Drivers --->
Graphics support --->
[ ] Samsung Exynos HDMI support
关掉后,内核体积能小个几百KB。别小看这几百KB,在嵌入式设备里,每一KB都珍贵。
4.4 配置系统的实战技巧
讲了这么多理论,来点实际的。我在Exynos项目里总结了几条经验:
| 场景 | 做法 | 原因 |
|---|---|---|
| 第一次配置 | 用defconfig起步 | 保证能启动,再逐步裁剪 |
| 裁剪驱动 | menuconfig里关掉不用的 | 直观,不容易漏 |
| 保存配置 | 用savedefconfig | 生成的文件小,方便版本管理 |
| 调试问题 | 检查.config里的依赖链 | 很多编译错误是依赖没配全 |
核心原则:配置系统不是一次搞定的。先让系统跑起来,再一点点关东西。每关一个选项,就编译测试一次。这样出了问题,你马上知道是哪个选项引起的。
最后说一句,make oldconfig 也是个好东西。当你从旧内核升级到新内核时,它会问你新增的选项怎么配。省得你从头配一遍。
嗯,配置系统就讲到这里。下一章咱们开始动手,用Exynos 4412的板子,一步步裁剪出一个最小内核。到时候你会真正体会到,今天讲的这些Kconfig和Makefile知识,到底有多重要。