4、内核配置与裁剪:内核Kconfig详解,基于项目需求裁剪内核模块,减少镜像体积

内核裁剪,说白了就是给Linux内核做「瘦身手术」。

很多刚入行的朋友觉得内核配置就是跑个make menuconfig,然后一路默认。嗯,我刚开始也这么干过。直到有一次,一个只有64MB Flash的IoT项目,默认内核镜像直接塞爆了存储空间——那次加班到凌晨三点,才意识到内核裁剪不是「要不要做」,而是「必须做」。

4.1 Kconfig语法速通:看懂内核的「菜单源码」

Kconfig是内核配置系统的「菜单描述语言」。你看到的menuconfig界面,背后全是Kconfig文件在驱动。

先看一个最简例子:

config MY_DRIVER
    bool "Enable My Custom Driver"
    default n
    help
      This is a custom driver for project X.

这段代码定义了一个开关选项。我来拆解一下:

  • config:关键字,声明一个配置项
  • MY_DRIVER:宏名称,最终会变成CONFIG_MY_DRIVER
  • bool:类型,表示二选一(y/n)
  • default n:默认关闭
  • help:帮助信息

实际项目中,Kconfig的写法要复杂得多。我整理了一份常用类型对照表:

类型 含义 典型场景
bool 二选一(y/n) 功能开关、驱动启用
tristate 三选一(y/m/n) 可编译为模块的驱动
int 整数输入 缓冲区大小、超时时间
hex 十六进制输入 内存地址、寄存器偏移
string 字符串输入 设备路径、固件名称
我的习惯:项目专用的驱动,一律用bool + default n。只有那些需要按需加载的第三方驱动,才用tristate。这样能避免「不小心把驱动编进内核」的尴尬。

4.2 依赖与反向依赖:别让配置「打架」

Kconfig最让人头疼的,就是依赖关系。我曾经在一个项目中,明明关了某个功能,编译时却报错说依赖缺失——查了半天,发现是另一个选项反向依赖了它。

看这个例子:

config USB_STORAGE
    tristate "USB Mass Storage support"
    depends on USB

config USB_STORAGE_REALTEK
    tristate "Realtek USB Card Reader"
    depends on USB_STORAGE
    select SCSI

这里有两个关键点:

  • depends on:前置依赖。USB_STORAGE必须等USB开启后才能选
  • select:反向依赖。选了Realtek读卡器,会自动把SCSI选上

你想想看,select其实是个「强制拉入」的机制。用得好可以简化配置,用不好就会引入一堆你根本不需要的东西。

我曾经踩过的坑:在一个ARM平台项目中,我select了一个REGULATOR驱动,结果它又select了I2C,I2C又select了GPIO……最后内核镜像比原来大了30%。所以我的建议是:能用depends on就别用select,除非你非常清楚依赖链的终点。

4.3 基于项目需求裁剪:从「全功能」到「刚刚好」

裁剪内核,不是盲目删东西。你得先搞清楚:你的硬件上有什么?你的业务需要什么?

我一般按这个流程走:

  1. 跑一次默认配置,生成.config
  2. 统计镜像大小,记录基线
  3. 逐模块审查,关闭不需要的驱动
  4. 反复编译验证,确保功能正常

下面这张图,是我做裁剪时的核心思路:

内核裁剪决策流程 硬件清单 业务需求 默认配置 交叉比对:保留硬件需要 + 业务需要的驱动 逐模块审查:关闭不需要的子系统、驱动、特性 编译验证:反复迭代,直到功能正常 + 体积达标 说明:裁剪不是一次完成的,通常需要3~5轮迭代

4.4 高频裁剪点:哪些模块最「占地方」?

根据我的项目经验,下面这几个地方是「减肥重灾区」:

子系统 典型裁剪项 可节省空间
网络协议栈 IPv6、IPSec、802.11、蓝牙 200KB ~ 1MB
文件系统 NTFS、XFS、Btrfs、FUSE 300KB ~ 800KB
驱动 显卡、声卡、网卡(非目标硬件) 500KB ~ 2MB
调试特性 printk、debugfs、trace、lockdep 100KB ~ 500KB
虚拟化 KVM、Xen、容器相关 200KB ~ 600KB
一个真实案例:我之前做智能网关项目,默认内核镜像4.2MB。裁剪掉IPv6、蓝牙、NTFS、显卡驱动、调试选项后,降到1.8MB。启动时间也从12秒缩短到6秒。说白了,裁剪不仅省空间,还能提速。

4.5 裁剪后的验证:别把「能用的」裁掉了

裁剪最怕什么?最怕把系统裁「残」了。

我建议你做三件事:

  • 功能测试:所有业务功能跑一遍,确保驱动正常加载
  • 启动日志检查:看dmesg里有没有failed to probe之类的错误
  • 对比.config:用diff对比裁剪前后的配置文件,确认没有误删
一个小技巧:每次裁剪前,把.config备份一份。万一裁坏了,cp回来就行,不用重新配。

4.6 用make savedefconfig生成最小配置

手动裁剪完,.config文件可能还有几千行。怎么把它变成「最小可读」的版本?

用这个命令:

make savedefconfig

它会生成一个defconfig文件,只保留非默认值的选项。说白了,就是「只记录你改过的东西」。

举个例子,原始.config有3000行,savedefconfig之后可能只剩50行。这个文件可以直接放到Buildroot的board/目录下,作为项目的默认内核配置。

注意:savedefconfig生成的配置,依赖内核版本。换内核版本时,最好重新生成一次。我吃过这个亏——升级内核后,旧defconfig直接编译不过。

4.7 本章小结

内核裁剪,说白了就是「做减法」。但减法不是乱减,你得懂Kconfig的语法,理清依赖关系,然后基于硬件清单和业务需求,一刀一刀地裁。

我个人习惯是:先裁掉所有不用的驱动,再关掉调试特性,最后优化文件系统和网络协议栈。每裁一步,编译一次,验证一次。虽然慢,但稳。

嗯,记住一句话:能跑的最小内核,才是最好的内核。