第4章 内核配置基础:menuconfig、Kconfig 与 .config
内核配置,说白了就是告诉编译器:哪些功能要编进去,哪些不要。我见过不少新手直接拿厂家给的 defconfig 一编译了事,结果调外设时发现驱动根本没编进去,又得从头来。这一章咱们就把配置的底子打牢。
4.1 menuconfig:你与内核的对话窗口
运行 make menuconfig 后,你会看到一个蓝底黄字的界面。嗯,这玩意儿看着复古,但效率极高。我个人习惯在修改驱动前,先跑一遍 menuconfig 确认当前配置状态。
界面操作其实就几个键:
- 方向键:上下移动光标,左右切换底部按钮
- Enter:进入子菜单 / 确认选择
- Y / N / M:编入内核 / 不编 / 编译为模块
- ?:查看选项的帮助信息——这个我经常用
- /:搜索功能,按名字找选项
/ 输入关键词,比如搜 "I2C",会列出所有含 I2C 的选项及其路径。我在 Rockchip 平台上调摄像头时,经常用这个快速定位驱动开关。
选项前面的标记有讲究:
| 标记 | 含义 | 说明 |
|---|---|---|
[ ] | 三态(未选) | 按 Y 编入,M 变模块,N 取消 |
[M] | 模块化 | 编译为 .ko 文件,运行时加载 |
[*] | 编入内核 | 直接链接到 zImage,启动时自带 |
{ } | 强制依赖 | 父选项选中后,子选项自动固定 |
我曾经在调试 RK3588 的 PCIe 时,发现选项是 <M> 但死活加载不了。后来才发现父选项没开,导致子选项虽然选了 M,实际根本没编译。所以,检查依赖关系是排查配置问题的第一步。
4.2 Kconfig 语法:配置规则的“源代码”
每个驱动目录下都有 Kconfig 文件。menuconfig 的菜单结构,就是靠这些文件拼出来的。说白了,Kconfig 就是配置的“语法规则书”。
看一个 Rockchip 平台常见的例子:
config ROCKCHIP_I2S
tristate "Rockchip I2S controller driver"
depends on ARCH_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE
help
Say Y here to enable Rockchip I2S controller driver.
This driver supports I2S/PCM interface for audio.
逐行拆解:
config ROCKCHIP_I2S:选项名,最终在 .config 里变成CONFIG_ROCKCHIP_I2Stristate:三态(Y/N/M)。如果是bool,就只有 Y/N,不能编模块depends on:依赖条件。ARCH_ROCKCHIP 没选,这个选项根本看不见select:反向依赖。选中这个选项时,自动强制选上 SND_SOC_GENERIC_DMAENGINEhelp:帮助信息,menuconfig 里按 ? 看到的就是它
再来看一个带 choice 的写法,Rockchip 的 DDR 频率策略里常见:
choice
prompt "DDR Frequency Scaling Governor"
depends on ROCKCHIP_DDRFREQ
config DDR_GOVERNOR_PERFORMANCE
bool "Performance"
help
Always run at max frequency.
config DDR_GOVERNOR_ONDEMAND
bool "Ondemand"
help
Scale frequency based on load.
endchoice
choice 是单选组,里面只能选一个。我调 RK3566 的低功耗模式时,就踩过这个坑——选了 Performance 后功耗一直下不来,后来才发现是 choice 里默认选了它。
4.3 .config 文件结构:最终配置清单
运行完 menuconfig 并保存,根目录下会生成 .config。这文件就是内核编译的“购物清单”。
打开看看,格式很简单:
#
# Automatically generated file; DO NOT EDIT.
# Linux/arm64 6.1.0 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="aarch64-linux-gnu-gcc (Linaro) 7.5.0"
CONFIG_ARM64=y
CONFIG_ARCH_ROCKCHIP=y
CONFIG_ROCKCHIP_I2S=m
CONFIG_I2C_ROCKCHIP=y
# CONFIG_ROCKCHIP_THERMAL is not set
关键点:
=y:编入内核=m:编译为模块is not set:没选,前面带#注释掉- 以
#开头的行,除了注释,就是没选的选项
我有个习惯:每次改完配置,先 diff .config .config.old 看看改了啥。这能帮你快速定位是不是误操作了某个选项。
4.4 常用配置操作速查
| 操作 | 命令 | 说明 |
|---|---|---|
| 图形化配置 | make menuconfig | 最常用,依赖 ncurses 库 |
| 基于现有配置修改 | make menuconfig | 会自动读取当前 .config |
| 使用默认配置 | make rockchip_linux_defconfig | 厂家提供的模板,在 arch/arm64/configs/ 下 |
| 保存当前配置 | menuconfig 内选 Save | 覆盖 .config |
| 备份配置 | cp .config my_backup.config | 我每次调通一个新板子都会备份一份 |
| 命令行改单项 | scripts/config --enable CONFIG_XXX | 适合脚本批量修改 |
make rockchip_linux_defconfig 生成基础配置,然后 make menuconfig 按需打开我要调试的驱动,最后 diff .config .config.old 确认改动。这套流程帮我省了不少排查时间。
嗯,内核配置这块儿,说白了就是三板斧:知道界面怎么操作,看得懂 Kconfig 语法,读得懂 .config 内容。把这章吃透了,后面调驱动时你就能快速定位“是配置没开”还是“代码本身有问题”。