第四章 内核配置系统详解: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里按?就能看到

重要提醒selectdepends 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

你会看到一个分层的菜单树。上下键移动,空格键切换选择,回车进入子菜单。

我个人习惯的流程是:

  1. 先用 make exynos_defconfig 加载默认配置
  2. 再用 make menuconfig 微调
  3. 最后 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知识,到底有多重要。