4. 内核配置与编译:从Kconfig到设备树

内核配置与编译,说白了就是给RK3568量身定制一套操作系统内核。你想想看,一个通用Linux内核有几万种驱动,但你的板子只需要其中一小部分。怎么把不需要的砍掉,把需要的加进来?这就是本章要解决的问题。

我个人习惯把内核配置比作「点菜」——Kconfig是菜单,menuconfig是点菜界面,defconfig是套餐,编译就是后厨炒菜。咱们一个一个来聊。

4.1 Kconfig体系:内核的「菜单」是怎么写的

Kconfig不是配置文件,它是一种脚本语言。每个驱动目录下都有一个Kconfig文件,描述了这个驱动有哪些配置选项、依赖什么其他选项、默认值是什么。

举个例子,RK3568的GMAC以太网驱动,它的Kconfig片段长这样:

config RK_GMAC
    tristate "Rockchip GMAC Ethernet support"
    depends on ARCH_ROCKCHIP
    select MII
    select PHYLIB
    help
      This driver supports the Rockchip GMAC ethernet controller.
      Say Y here if you need ethernet support.

这里有几个关键字段:

  • tristate:三态选项——Y(编入内核)、M(编译成模块)、N(不编译)
  • depends on:依赖关系,ARCH_ROCKCHIP没选,这个选项就不显示
  • select:反向依赖,选了GMAC就自动选上MII和PHYLIB
  • help:帮助信息,menuconfig里按H键能看到
我的习惯: 写驱动时先看Kconfig,能快速了解这个驱动依赖哪些子系统。有一次我移植一个USB摄像头驱动,死活编不过,后来发现是缺少VIDEO_V4L2的select——这就是不看Kconfig的代价。

4.2 menuconfig图形化配置:可视化点菜

在源码根目录执行 make menuconfig,就能看到那个经典的蓝白界面。它本质上是把Kconfig解析成树形菜单,让你用方向键和空格键来配置。

常用操作键:

按键 功能
↑ ↓ 移动光标
Space 切换选项(N→M→Y→N循环)
Y / N / M 直接设置
/ 搜索(按名字找选项)
Z 显示所有选项(包括被依赖隐藏的)

搜索功能特别实用。比如你想找RK3568的I2C驱动,按/输入I2C_RK3X,它会告诉你这个选项在哪个菜单路径下,当前是什么状态。

注意: menuconfig修改后,配置保存在.config文件里。这个文件是纯文本,你也可以直接编辑,但不建议——格式很严格,手写容易出错。我见过有人手改.config导致编译到一半崩溃的。

4.3 defconfig:官方「套餐」与定制

每个芯片厂商都会提供一个默认配置,放在arch/arm64/configs/目录下。RK3568的官方defconfig叫rockchip_linux_defconfig。

使用方式很简单:

make ARCH=arm64 rockchip_linux_defconfig

这条命令会把rockchip_linux_defconfig复制为.config。之后你再跑make menuconfig做微调。

我建议的工作流是这样的:

  1. make rockchip_linux_defconfig —— 用官方套餐打底
  2. make menuconfig —— 按需增删驱动
  3. make savedefconfig —— 生成精简后的defconfig
  4. 把生成的defconfig重命名,保存到版本管理里
为什么要savedefconfig? 因为.config里有很多默认值选项,体积很大。savedefconfig只保留你修改过的选项,方便代码审查和版本管理。

我曾经在一个项目里,团队五个人各自改.config,结果合代码时冲突不断。后来强制用savedefconfig + 统一defconfig,问题就解决了。

4.4 编译内核与设备树

配置搞定后,编译就相对机械了。RK3568是ARM64架构,编译命令如下:

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make -j$(nproc) Image
make -j$(nproc) dtbs
make -j$(nproc) modules

解释一下:

  • Image:内核镜像,启动时由uboot加载
  • dtbs:设备树二进制文件,描述硬件拓扑
  • modules:内核模块,按需加载的驱动

编译产物在arch/arm64/boot/目录下:

文件 说明
Image 未压缩的内核镜像
Image.gz gzip压缩的内核镜像(更小,uboot支持解压)
dts/ 设备树源文件(.dts/.dtsi)
dts/rockchip/ RK平台设备树源文件

设备树编译是自动的——你改了.dts文件,跑make dtbs就会重新生成对应的.dtb。RK3568的设备树文件是rk3568-evb.dts或rk3568-rock-3a.dts,具体看你用的板子。

调试技巧: 编译时加V=1能看到完整编译命令,方便排查头文件路径、链接顺序等问题。我调试一个PCIe驱动时,就是靠V=1发现少了-I路径。

4.5 知识体系总览

下面这张图把整个配置编译流程串起来了:

内核配置与编译流程 Kconfig 文件 defconfig 文件 DTS 源文件 menuconfig make defconfig dtc 编译器 .config .config(已展开) .dtb 文件 make Image / make modules Image + .dtb + modules savedefconfig 精简配置

这张图把整个流程分成了五层:输入文件 → 配置工具 → 中间产物 → 编译 → 最终输出。你跟着这个流程走,基本不会出错。

4.6 常见问题与避坑

编译过程中我遇到过不少坑,挑几个典型的说说:

  • 编译报错说找不到头文件:多半是交叉编译链没装对,或者CROSS_COMPILE环境变量没设。检查aarch64-linux-gnu-gcc --version能不能跑。
  • menuconfig里找不到某个选项:先确认depends on的依赖选项有没有打开。按/搜索,看它被什么依赖隐藏了。
  • 设备树编译报语法错误:DTS对分号和花括号很敏感,少一个分号就报错。我习惯用dtc -I dts -O dtb -o /dev/null test.dts做语法检查。
  • 编译出来的内核启动卡住:大概率是.config里砍掉了某个必要驱动。比如串口驱动没编进去,你连内核日志都看不到。
我曾经踩过的坑: 有一次我为了缩小内核体积,把CONFIG_DEVTMPFS关了。结果系统启动后/dev目录是空的,所有设备节点都没有。折腾了半天才发现是这个问题。所以,不熟悉的选项不要乱改,先查文档。

嗯,内核配置编译这块内容就这些。你只要把Kconfig、menuconfig、defconfig这三者的关系理清楚,再记住编译命令,剩下的就是多练。每个板子的defconfig可能不一样,但套路是一样的。