3、内核配置系统详解:menuconfig图形化配置、defconfig与.config文件、配置项依赖关系
内核配置,说白了就是决定「我的芯片上要跑哪些功能,砍掉哪些包袱」。
我见过不少新手,拿到一个开发板就直接 make menuconfig 乱选一通,结果编译出来的内核镜像比整个 Flash 还大,或者某个驱动死活不工作——其实就是配置依赖没理清楚。
这一章,我就带你把这套配置系统彻底搞明白。你以后裁剪内核、适配外设,心里就有底了。
3.1 menuconfig:图形化配置界面
在 Tina Linux 的 SDK 根目录下,执行:
make menuconfig
你会看到一个蓝底灰字的界面。嗯,别嫌它丑,这玩意儿在嵌入式领域可是标配。
它的操作逻辑其实很简单:上下键移动、空格键切换选中状态、回车键进入子菜单。
每个配置项前面有三种状态:
| 符号 | 含义 | 说明 |
|---|---|---|
[ ] |
不编译 | 该功能被完全排除 |
[M] |
编译为模块 | 以 .ko 形式存在,动态加载 |
[*] |
编译进内核 | 直接内建到 zImage 中 |
我个人习惯是:能编进内核的就别用模块。为什么?
嵌入式设备资源有限,模块加载还要额外的文件系统和启动脚本,徒增复杂度。除非你确实需要动态插拔驱动,否则一律 [*]。
/ 键可以搜索配置项名称。比如你想找 I2C 相关的配置,输入 I2C 回车,所有包含 I2C 的选项都会列出来,连路径都给你标好了。
3.2 defconfig 与 .config:两种配置文件的博弈
你每次在 menuconfig 里改完配置,保存后其实只生成了一个文件——.config。
但这个文件是「临时」的。如果你执行 make distclean,它就被删了。
那怎么让配置「固化」下来?
答案就是 defconfig。
在 Tina Linux 中,每个芯片平台都有自己的默认配置,存放在:
target/allwinner/<芯片型号>/defconfig
比如 target/allwinner/v853/defconfig。
这个文件是「精简版」的 .config,只记录与默认值不同的选项。它的好处是:可读性强、体积小、适合版本管理。
我举个例子你就明白了:
| 对比项 | .config | defconfig |
|---|---|---|
| 文件大小 | 几千行 | 几十到几百行 |
| 记录方式 | 所有选项 | 仅差异项 |
| 用途 | 实际编译 | 配置模板 |
| 是否可删除 | 可重新生成 | 建议保留 |
工作流程是这样的:
make defconfig—— 从 defconfig 生成 .configmake menuconfig—— 手动微调 .configmake savedefconfig—— 把当前 .config 的差异写回 defconfig
我曾经在一个量产项目上吃过亏:同事直接改了 .config 就提交到 Git,结果别人拉下来 make distclean 后配置全丢了。从那以后,我要求团队必须用 savedefconfig 更新 defconfig,.config 永远不入库。
y 为 n,可能连带把十几个依赖项都搞崩了。老老实实用 menuconfig 或者 scripts/config 工具。
3.3 配置项依赖关系:为什么你选不了某个选项?
你有没有遇到过这种情况:
进入 menuconfig,看到一个功能,想选 [*],结果按空格没反应,选项是灰色的?
这不是 bug,是 依赖关系 在起作用。
每个配置项在 Kconfig 文件中定义时,都可以声明它依赖哪些其他选项。比如:
config TOUCHSCREEN_GT9XX
tristate "Goodix GT9xx touchscreen"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
help
Say Y here if you have a Goodix GT9xx touchscreen.
这段代码的意思是:
- 要选 GT9xx 触摸驱动,必须先开启 I2C
- 同时需要 GPIOLIB 或者处于编译测试模式
如果 I2C 没开,GT9xx 的选项就是灰色的,你根本选不了。
依赖关系有三种常见形式:
| 关键字 | 含义 | 示例 |
|---|---|---|
depends on |
强依赖 | 不满足则无法选中 |
select |
反向选择 | 选中本项时自动开启另一项 |
imply |
弱建议 | 建议开启,但不强制 |
你想想看,如果每个驱动都独立无依赖,那内核配置界面会乱成什么样?
依赖链其实是在保护你——防止你选了一个「孤零零」的功能,编译出来缺胳膊少腿。
FB_TFT 选项死活选不了。查了 Kconfig 才发现它 depends on SPI,而 SPI 子系统默认没开。先回去把 SPI 选上,再回来就能选了。这种「先开父项,再选子项」的思路,你以后会经常用到。
3.4 知识结构图:配置系统的核心逻辑
下面这张图,把整个配置系统的流转关系画清楚了:
3.5 避坑指南:我踩过的三个配置坑
做内核配置这么多年,有些坑我至今记忆犹新。分享给你,希望你少走弯路。
- 坑一:依赖链断裂
有一次我裁剪内核,把INPUT子系统关了,结果触摸屏、按键、鼠标全都不工作。查了半天才发现,所有输入设备都depends on INPUT。嗯,这个教训让我学会了「先看依赖树,再动手裁剪」。 - 坑二:模块与内建混用导致启动失败
我把根文件系统需要的驱动(比如 SD 卡控制器)编成了模块[M],结果内核启动时根本挂载不了根文件系统——因为模块在根文件系统里,而根文件系统又需要这个模块才能访问。死循环了。所以,启动必需的驱动一定要编进内核。 - 坑三:defconfig 与 .config 不同步
团队协作时,有人改了 .config 但没执行savedefconfig,导致其他人拉代码后编译出来的内核行为不一致。后来我在 CI 流程里加了一步:每次提交前自动检查 defconfig 是否与 .config 同步。
make savedefconfig,然后把 defconfig 提交到 Git。这个习惯能帮你省掉很多排查「为什么你的内核和我的不一样」的时间。
好了,这一章的内容就到这里。配置系统是内核裁剪的基石,你把 menuconfig、defconfig、.config 和依赖关系搞透了,后面裁剪内核、适配外设就会顺手很多。