第3章 内核配置入门:menuconfig图形化配置、defconfig与dts分离、内核模块与内建驱动选择策略
说实话,很多刚接触Rockchip平台的朋友,第一件事就是拿个默认配置文件,make menuconfig进去瞎改一通。改完编译,烧进去,系统起不来——然后就开始怀疑人生。
我当年也干过这种事。记得第一次给RK3288做BSP,我把所有驱动都编进内核,结果内核镜像大了快一倍,启动慢得像蜗牛。后来才明白,内核配置这件事,看似简单,其实门道很深。
这一章,我就带你把这些门道捋清楚。
3.1 menuconfig图形化配置:不只是点点菜单
make menuconfig是Linux内核配置的标配工具。但很多人只把它当成一个菜单浏览器,其实它的交互逻辑里藏着不少信息。
3.1.1 基本操作与符号含义
进入menuconfig后,你会看到各种选项前面有不同符号。这些符号代表什么?我整理了一张表:
| 符号 | 含义 | 说明 |
|---|---|---|
[ ] |
未选中 | 该功能不编译 |
[*] |
内建 | 编译进内核镜像 |
<M> |
模块 | 编译为独立.ko文件 |
< > |
未选择 | 与[ ]类似,但可切换为M |
---> |
子菜单 | 按Enter进入 |
这里有个小技巧:按 / 键可以搜索配置项。比如你想找RK3588的MIPI DSI驱动,直接按 / 输入 DSI,所有相关选项就列出来了。这个功能我几乎每天都在用。
? 查看选项的帮助信息。很多坑都在帮助文档里写着,比如依赖关系、需要哪些前置驱动。别嫌麻烦,这一步能省你半天调试时间。
3.1.2 依赖关系与反向依赖
menuconfig里最让人头疼的就是依赖关系。你选了一个选项,结果它自动帮你选了另外一堆。有时候你取消一个选项,又发现其他选项跟着变灰了。
为什么会这样?因为内核的Kconfig系统有 depends on 和 select 两种机制:
- depends on:当前选项依赖某个选项被选中。如果依赖项没选,当前选项不可见或不可选。
- select:当前选项被选中时,自动选中另一个选项。这是反向依赖,常用于硬件平台必须启用的功能。
我在做RK3566项目时遇到过一个问题:我明明选了GPU驱动,但menuconfig里死活找不到Mali驱动选项。后来查了Kconfig文件才发现,它 depends on DRM,而DRM我没开。这就是典型的依赖链断裂。
.config 文件来绕过依赖检查。我曾经这么干过,结果编译出来的内核在运行时直接panic。依赖关系是硬约束,绕不过去的。
3.2 defconfig与dts分离:Rockchip平台的标准做法
Rockchip平台的内核配置,有一个非常清晰的架构:defconfig负责软件功能开关,dts负责硬件描述。这两者必须分离,不能混在一起。
3.2.1 为什么需要分离?
你想想看,一块RK3588芯片,可以做成开发板、平板、电视棒、NAS主板……每种产品的硬件都不一样。如果每次改硬件都要重新配内核,那工作量就太大了。
分离的好处很明显:
- defconfig:定义芯片平台通用的功能开关,比如文件系统、网络协议栈、调度器类型。这些跟具体硬件无关。
- dts:描述具体硬件,比如GPIO复用、I2C地址、时钟频率。这些跟具体板子相关。
在Rockchip的SDK里,你通常能看到这样的文件结构:
arch/arm64/configs/
rockchip_linux_defconfig # 通用配置
rockchip_linux_evb1_defconfig # EVB板专用配置
arch/arm64/boot/dts/rockchip/
rk3588.dtsi # 芯片级描述
rk3588-evb1.dts # 板级描述
rk3588-evb1-lp4.dts # 不同内存配置的变体
3.2.2 如何正确管理defconfig?
我个人习惯的做法是:
- 从官方defconfig开始:
make rockchip_linux_defconfig - 用menuconfig做增量修改:只改你需要的选项
- 保存修改后的配置:
make savedefconfig,生成精简版defconfig - 对比差异:用diff工具看改了哪些
这里有个关键点:make savedefconfig 会生成一个 defconfig 文件,它只包含非默认值的选项。这个文件比完整的 .config 小得多,也更容易维护。
3.2.3 dts的覆盖机制
Rockchip的dts采用多层覆盖结构:
rk3588.dtsi # 芯片级:CPU、GPU、内存控制器、时钟
└── rk3588-evb1.dtsi # 板级公共:UART、I2C、GPIO默认配置
└── rk3588-evb1.dts # 具体产品:差异化配置
这种结构的好处是,芯片升级时只需要改 .dtsi,板级文件基本不用动。我在RK3399升级到RK3588时,就靠这个机制省了大量移植工作。
3.3 内核模块与内建驱动选择策略
这是内核配置里最需要权衡的部分。选模块还是选内建,没有绝对的对错,只有合不合适的场景。
3.3.1 内建(built-in)的适用场景
内建驱动会直接编译进内核镜像,系统启动时自动初始化。适合以下情况:
- 根文件系统驱动:比如MMC/SD卡、SATA、NVMe。如果这些驱动是模块,内核就挂载不了根文件系统。
- 系统核心功能:比如中断控制器、定时器、时钟管理。这些必须在系统启动早期就可用。
- 实时性要求高的设备:模块加载有延迟,内建驱动没有。
3.3.2 模块(module)的适用场景
模块是独立的 .ko 文件,可以在系统运行时动态加载或卸载。适合以下情况:
- 不常用的外设:比如蓝牙、WiFi、USB摄像头。平时不用就不加载,节省内存。
- 调试中的驱动:开发阶段用模块,方便反复修改和测试,不用每次都重编内核。
- 需要热插拔的设备:模块可以按需加载,配合udev自动处理。
3.3.3 Rockchip平台的实际选择策略
基于我在多个Rockchip项目上的经验,我总结了一套选择策略:
| 驱动类型 | 推荐方式 | 原因 |
|---|---|---|
| MMC/SDIO | 内建 | 根文件系统依赖 |
| eMMC控制器 | 内建 | 系统启动必须 |
| DDR控制器 | 内建 | 内存管理核心 |
| GPU驱动 | 模块 | 体积大,按需加载 |
| VPU编解码 | 模块 | 多媒体场景才需要 |
| WiFi/BT | 模块 | 热插拔,可卸载 |
| USB控制器 | 内建 | 早期初始化,支持键盘/鼠标 |
| 以太网MAC | 内建 | 网络根文件系统(NFS)需要 |
lsmod 看哪些模块被自动加载了。如果某个模块每次启动都加载,那就说明它应该内建。这叫"从实践中来,到实践中去"。
3.3.4 模块加载顺序的坑
模块之间也有依赖关系。比如一个I2C设备驱动,依赖I2C总线驱动先加载。如果顺序错了,设备就找不到。
内核通过 modules.dep 文件管理依赖关系。你可以在编译后查看:
# 查看模块依赖
modinfo rk3588-mipi-dsi.ko
# 输出中会显示 depends: drm, phy-rockchip-samsung-hdptx
# 手动加载时,依赖模块会自动加载
modprobe rk3588-mipi-dsi
这里要注意:modprobe 会自动处理依赖,但 insmod 不会。所以生产环境一定要用 modprobe。
insmod 加载驱动,结果报错说找不到 drm_panel 符号。查了半天才发现,drm_panel 是另一个模块,必须先加载。换成 modprobe 就一切正常了。
3.4 本章知识体系总览
为了让你更直观地理解本章的知识结构,我画了一张流程图:
3.5 实战:从零配置一个Rockchip内核
光说不练假把式。我们走一遍完整的配置流程:
- 选择基础defconfig:
make ARCH=arm64 rockchip_linux_defconfig - 进入menuconfig做定制:
比如你要添加一个SPI显示屏驱动,按make ARCH=arm64 menuconfig/搜索SPI,找到对应选项设为<M>。 - 保存精简配置:
make ARCH=arm64 savedefconfig cp defconfig arch/arm64/configs/my_board_defconfig - 编写dts文件:
// arch/arm64/boot/dts/rockchip/my-board.dts #include "rk3588.dtsi" #include "rk3588-evb1.dtsi" / { model = "My Custom Board"; compatible = "my,board", "rockchip,rk3588"; }; &spi2 { status = "okay"; display: ili9341@0 { compatible = "ilitek,ili9341"; reg = <0>; spi-max-frequency = <40000000>; }; }; - 编译并验证:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8 # 检查模块是否生成 find . -name "*.ko" | grep ili9341
V=1 参数,能看到详细的编译命令。如果报错,能快速定位是哪个文件出了问题。
嗯,这一章的内容就到这里。内核配置看似是体力活,但背后有清晰的逻辑。你只要掌握了menuconfig的操作、defconfig与dts的分离原则、以及模块与内建的选择策略,Rockchip平台的内核配置对你来说就不再是难题。