内核配置基础:Kconfig 与 Makefile 的裁剪哲学

做嵌入式驱动开发,你迟早要面对一个问题:内核为什么这么大?

我刚开始接触 Linux 内核时,编译一次要等半小时。后来发现,里面有一大半驱动我这辈子都用不上。Wi-Fi 协议栈、显卡驱动、文件系统……我的板子就是个跑裸机的 Cortex-M,要这些干嘛?

所以,裁剪是必须的。而裁剪的根基,就是 Kconfig 和 Makefile。说白了,Kconfig 是「菜单」,Makefile 是「菜谱」。你点菜,厨师按菜谱做。今天我们就聊聊这两样东西的哲学——怎么点菜,怎么改菜谱,才能让内核又小又快。

Kconfig:内核的「点菜系统」

Kconfig 文件定义了内核的配置选项。每个驱动、每个功能,都有一个对应的配置项。比如:

config MY_DRIVER
    bool "Enable My Custom Driver"
    depends on ARM
    help
      This driver does something cool on ARM platforms.

这个配置项会出现在 make menuconfig 的界面里。你选中它,驱动就会被编译;不选,就跳过。

我个人习惯,在写驱动时一定会加 depends on 和 select。为什么?因为依赖关系不写清楚,别人配置时就会踩坑。我曾经见过一个同事,把 I2C 驱动配上了,但忘了配 I2C 核心模块,结果编译报错,查了半天。

核心原则:Kconfig 的裁剪哲学是「按需加载」。你不需要的功能,一个都不要选。选得越少,内核越小,启动越快。

Makefile:编译的「指挥棒」

Kconfig 决定了「要不要编译」,Makefile 决定了「怎么编译」。典型的写法:

obj-$(CONFIG_MY_DRIVER) += my_driver.o

如果 CONFIG_MY_DRIVER 被设为 y,my_driver.o 就会被编译进内核。如果是 m,就编译成模块。如果是空,就不编译。

这里有个小技巧:模块化 vs 内建。我建议,启动时必须用的驱动(比如串口、时钟)内建;不常用的(比如 USB 摄像头、Wi-Fi)做成模块。这样内核镜像小,启动快,需要时再加载模块。

我的经验:在项目初期,把所有驱动都编译成模块。调试时用 insmod 加载,方便快速迭代。等稳定了,再把关键驱动内建。

裁剪哲学:少即是多

裁剪不是盲目删东西。你得知道哪些是必需的,哪些是多余的。我总结了一个「三问法」:

  1. 这个驱动我的硬件需要吗?——不需要就关掉。
  2. 这个功能我的系统用得上吗?——用不上就关掉。
  3. 这个模块能延迟加载吗?——能就做成模块。

举个例子。我在做一款工业控制器时,板子上只有 SPI Flash、UART 和 GPIO。我直接把所有网络驱动、音频驱动、显卡驱动全关了。内核从 4MB 缩到 800KB,启动时间从 8 秒降到 2 秒。

注意:不要为了追求小内核而关掉必要的依赖。比如你选了 SPI 驱动,但忘了选 SPI 核心,编译会报错。Kconfig 的 depends on 就是干这个的,别绕过它。

Kconfig 与 Makefile 的协作流程

下面这张图展示了从配置到编译的完整流程:

Kconfig 与 Makefile 协作流程 Kconfig 与 Makefile 协作流程 Kconfig 定义选项 make menuconfig 生成 .config Makefile 读取 条件编译 生成 .o / .ko 说明:Kconfig 定义选项 → menuconfig 配置 → 生成 .config → Makefile 读取 → 条件编译 → 生成目标文件

常见裁剪策略对比

不同的场景,裁剪策略也不同。我整理了一个表格:

场景 裁剪策略 典型配置 效果
裸机系统 全内建,无模块 只选必要驱动 内核最小,启动最快
嵌入式 Linux 关键驱动内建,其余模块 串口、时钟内建;Wi-Fi、USB 模块 平衡大小与灵活性
开发板调试 全模块化 所有驱动编译为模块 方便热插拔调试
生产环境 严格裁剪 只保留硬件必需的驱动 安全、稳定、高效

避坑指南

做裁剪时,有几个坑我踩过,分享给你:

  • 依赖链断裂:我曾经关掉了一个看似无关的驱动,结果导致整个 USB 子系统罢工。后来发现那个驱动是 USB 核心的依赖。所以,关掉之前先看 depends on 和 select。
  • 模块加载顺序:如果驱动 A 依赖驱动 B,B 必须先加载。我建议用 modprobe 而不是 insmod,它会自动处理依赖。
  • 调试信息过多:开发时打开 DEBUG 没问题,但发布前一定要关掉。否则内核日志会被刷爆,性能也会下降。

小技巧:用 make savedefconfig 生成最小配置。它会从当前 .config 中提取出非默认的选项,方便你后续维护。

总结

Kconfig 和 Makefile 是内核裁剪的基石。Kconfig 定义「要不要」,Makefile 定义「怎么编」。裁剪的核心哲学是:只保留你需要的,其他的都砍掉。

嗯,说起来简单,做起来需要经验。但只要你掌握了这三步——分析需求、配置选项、验证编译——你就能做出又快又小的内核。