4. 内核配置系统详解:make config/menuconfig/xconfig/gconfig、.config文件结构、配置依赖与选择
内核配置,说白了就是给Linux内核“点菜”。你要什么功能,不要什么功能,全在这一步定下来。我见过不少新手,上来就make menuconfig一顿乱选,结果编译出来的内核要么缺驱动,要么大得离谱。嗯,这章我们就把它彻底讲透。
4.1 四种配置界面:你该用哪个?
Linux内核提供了四种配置工具,它们背后干的事一模一样——生成.config文件。区别只在于操作界面。
| 命令 | 界面类型 | 适用场景 | 我的评价 |
|---|---|---|---|
make config |
命令行问答 | 脚本自动化、无图形环境 | 反人类,但偶尔救命 |
make menuconfig |
终端ncurses菜单 | 日常开发、SSH远程 | 最常用,我90%的情况用它 |
make xconfig |
Qt图形界面 | 桌面环境、喜欢鼠标操作 | 搜索方便,但依赖Qt库 |
make gconfig |
GTK+图形界面 | GNOME用户 | 跟xconfig类似,看个人喜好 |
menuconfig,本地桌面用 xconfig。xconfig 的搜索功能(Ctrl+F)在找特定选项时特别快。
4.2 .config文件结构:内核的“购物清单”
不管你用哪种界面,最终都会生成一个.config文件。它就在内核源码根目录下,纯文本格式。我们来解剖一下它的结构。
# 注释行,以#开头
# ARM architecture
CONFIG_ARM=y # 选项被编译进内核(built-in)
CONFIG_MMU=y
# 模块选项
CONFIG_SOUND=m # 选项被编译为模块(module)
# 未选中选项
# CONFIG_VIDEO_DEV is not set # 该功能被禁用
# 字符串/整数选项
CONFIG_DEFAULT_HOSTNAME="(none)"
CONFIG_HZ=100
每个选项有三种状态:
- y —— 编译进内核镜像,直接可用
- m —— 编译成独立模块,需要时加载
- 空(未设置) —— 不编译,功能禁用
m。只有那些被标记为“可模块化”的驱动或功能才行。核心机制(如调度器、内存管理)只能 y 或 n。
4.3 配置依赖与选择:为什么我选了A,B却消失了?
这是新手最容易懵的地方。你明明在menuconfig里看到了某个选项,勾上之后保存退出,再进来发现它又没了。怎么回事?
原因很简单:内核选项之间有依赖关系。Kconfig语法里用 depends on 和 select 来控制。
4.3.1 depends on —— 依赖关系
如果A依赖B,那么B没选,A就不会出现。举个例子:
config USB_STORAGE
tristate "USB Mass Storage support"
depends on USB && SCSI
这意味着你必须先开启 USB 和 SCSI 支持,才能看到 USB_STORAGE 选项。
4.3.2 select —— 反向选择
有时候选了A,会自动把B选上。这叫“select”,常用于硬件依赖。比如:
config ARCH_MXC
bool "Freescale i.MX"
select ARM_CPU_SUSPEND
select ARM_GIC
选了i.MX架构,ARM_CPU_SUSPEND 和 ARM_GIC 会被自动勾上。你没法手动取消它们。
MEDIA_SUPPORT 和 V4L2。依赖链没打通,选项根本不会显示。
4.4 Kconfig语法速览:看懂内核的“菜单源码”
每个内核子目录下都有个 Kconfig 文件,它就是配置菜单的源代码。看懂它,你就能理解为什么某些选项会“灰掉”或“消失”。
menu "Device Drivers"
config SAMPLE_DRIVER
tristate "Sample driver support"
default n
help
This is a sample driver for teaching.
Say Y here if you want to build it into kernel.
Say M if you want to build it as module.
endmenu
关键字段:
- tristate —— 三态:y/m/n,用于驱动类选项
- bool —— 二态:y/n,用于功能开关
- default —— 默认值,可以是 y/m/n
- depends on —— 前置依赖
- select —— 自动选中其他选项
- help —— 帮助文本
4.5 配置依赖关系图
下面这张图展示了内核配置选项之间的典型依赖关系。你想想看,一个驱动要工作,往往需要层层依赖都打通。
4.6 实战技巧:如何高效配置内核
配置内核不是让你从头到尾把所有选项看一遍。那样会疯掉的。我分享几个实战方法。
4.6.1 基于现有配置修改
别从零开始。找一份跟你硬件接近的配置文件:
# 查看当前架构的默认配置
make defconfig
# 或者用厂商提供的配置
make vendor_defconfig
# 然后在此基础上 menuconfig 微调
4.6.2 搜索功能是你的好朋友
在 menuconfig 里按 / 键,可以搜索选项名或描述。比如你想找“SPI”相关的所有选项:
/spi
它会列出所有包含“spi”的选项,以及它们的依赖关系和当前位置。这个功能我几乎天天用。
4.6.3 保存与比较配置
每次修改后,建议把 .config 备份一份:
cp .config config_$(date +%Y%m%d_%H%M)
make savedefconfig # 生成精简版配置
savedefconfig 会去掉所有默认值,只保留你修改过的选项。这个文件更小,也更容易对比差异。
.config 用 git 管理起来。哪天改出问题了,git diff 一眼就能看出改了哪里。
4.7 常见配置问题与排查
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 选项在menuconfig中看不到 | 依赖的父选项未开启 | 按 / 搜索,查看依赖链 |
| 选项是灰色的无法选中 | 依赖条件不满足 | 检查 depends on 中的选项是否都开启了 |
| 选了A,B自动被勾上 | A 的 select 语句选中了 B |
查看 Kconfig 中 A 的 select 列表 |
| 编译报错:undefined reference | 某个依赖选项没开,但代码里用了 | 检查 .config 中相关选项是否为 y 或 m |
make allyesconfig 或 make allmodconfig。前者会把所有功能编译进内核,体积巨大;后者把所有能模块化的都编成模块。这两个只适合做编译测试,不适合实际运行。
内核配置系统,说白了就是一套“菜单+依赖”的规则引擎。你理解了 depends on 和 select,就掌握了它的核心。剩下的,就是多动手、多踩坑。我当年第一次配内核,忘了开网卡驱动,结果板子启动后连不上网络,折腾了一下午才找到原因。嗯,从那以后,我再也不敢小看 .config 了。