3. Linux 内核编译:从源码到镜像的完整旅程

说实话,内核编译这件事,很多初学者一听就觉得头大。我当年第一次接触时也差不多——对着满屏的配置选项发懵,编译一次等半小时,最后还起不来。但你别怕,这东西说白了就是一套流程,走通了就顺了。

这一章,我带你把整个流程捋一遍:源码从哪来、怎么配、怎么编、最后生成什么。每一步我都会告诉你实际项目中容易踩的坑。

3.1 内核源码获取

RK3568 的内核源码,官方推荐从 Rockchip 的 GitHub 仓库拉取。我个人习惯用 git clone,因为后续切分支、打补丁都方便。

核心仓库地址:

https://github.com/rockchip-linux/kernel.git

建议拉取 rk356x 或 linux-5.10.y 分支。

具体操作:

  1. 克隆仓库:git clone https://github.com/rockchip-linux/kernel.git -b rk356x
  2. 进入目录:cd kernel
  3. 查看分支:git branch -a,确认当前在 rk356x 上

我的经验:别用 master 分支。Rockchip 的 master 经常是旧版,rk356x 才是针对性的。我之前有一次图省事拉了 master,结果编译出来的内核在板子上死活起不来,查了两天才发现是 DTS 不匹配。

3.2 内核配置

源码到手后,下一步就是配置。配置决定了你的内核支持哪些功能、驱动、文件系统。说白了,就是告诉编译器:「我要这些东西,不要那些东西」。

RK3568 官方提供了一套默认配置,存放在 arch/arm64/configs/ 下。我常用的命令是:

make ARCH=arm64 rockchip_linux_defconfig

这条命令会生成一个 .config 文件,里面是几千个配置选项。嗯,你没看错,几千个。但别慌,大部分默认值已经对了。

3.2.1 常用配置命令

命令 作用 适用场景
make menuconfig 基于 ncurses 的图形化菜单 日常修改,最常用
make defconfig 使用架构默认配置 快速生成基础配置
make savedefconfig 将当前配置精简保存 生成最小化 defconfig
make oldconfig 基于旧配置更新新选项 内核版本升级时使用

注意:如果你在 menuconfig 里改了配置,记得保存退出。我曾经有一次改完直接关终端,结果白改了一小时。

3.2.2 配置要点

对于 RK3568 驱动开发,你至少需要关注以下几类配置:

  • 设备树(Device Tree):CONFIG_ARCH_ROCKCHIP=y,这是基础
  • GPIO 驱动:CONFIG_GPIO_ROCKCHIP=y
  • I2C/SPI 总线:根据你的外设选择
  • Pinctrl:CONFIG_PINCTRL_ROCKCHIP=y
  • 时钟框架:CONFIG_COMMON_CLK_ROCKCHIP=y

你想想看,如果这些基础配置没开,你后面写的驱动根本编译不进去。我在项目里见过有人折腾了一周,最后发现是 CONFIG_I2C_ROCKCHIP 没开——这种低级错误,犯一次就够了。

3.3 编译流程

配置搞定后,就是编译。RK3568 是 64 位 ARM 架构,所以需要指定交叉编译工具链。我一般这样写:

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

解释一下:

  • ARCH=arm64:指定架构
  • CROSS_COMPILE:交叉编译工具链前缀
  • -j$(nproc):用所有 CPU 核心并行编译,快很多
  • Image:生成内核镜像
  • dtbs:编译设备树二进制文件
  • modules:编译内核模块

完整编译脚本示例:

#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin

make rockchip_linux_defconfig
make -j$(nproc) Image dtbs modules 2>&1 | tee build.log

第一次编译大概需要 10-20 分钟,取决于你的机器性能。我建议你泡杯咖啡,或者看看文档,别干等。

3.3.1 编译产物

编译完成后,你会得到以下关键文件:

文件 路径 说明
Image arch/arm64/boot/Image 内核镜像,未压缩
Image.gz arch/arm64/boot/Image.gz 压缩后的内核镜像
*.dtb arch/arm64/boot/dts/rockchip/ 设备树二进制文件
*.ko 各驱动目录下 内核模块文件

避坑指南:我曾经有一次编译完只拷贝了 Image,忘了拷贝 .dtb 文件,结果板子启动时内核 panic,提示找不到根文件系统。后来才意识到,设备树里定义了内存布局和串口地址,没有它内核连控制台都打不开。

3.4 生成镜像文件

编译出来的 Image 和 .dtb 还不能直接烧录到板子上。RK3568 的启动流程需要特定的镜像格式。通常我们需要打包成 boot.img 或 recovery.img。

Rockchip 官方提供了打包工具 mkbootimg,用法如下:

mkbootimg --kernel arch/arm64/boot/Image \
          --ramdisk ramdisk.img \
          --dtb arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtb \
          --pagesize 2048 \
          --base 0x00200000 \
          -o boot.img

参数说明:

  • --kernel:内核镜像路径
  • --ramdisk:根文件系统镜像(可选)
  • --dtb:设备树文件
  • --pagesize:页大小,RK3568 通常为 2048
  • --base:内存基地址,RK3568 为 0x00200000
  • -o:输出文件名

重要:--base 参数必须与内核配置中的 CONFIG_PAGE_OFFSET 一致,否则内核无法正确解压。我见过有人把 base 写成 0x00000000,结果内核加载后直接跑飞。

3.5 知识结构总览

下面这张图,把整个内核编译的流程和产物关系画清楚了。你可以把它当作一张「地图」,每次编译前扫一眼,心里就有数了。

Linux内核编译流程与产物关系图 Linux 内核编译流程与产物关系 源码获取 (git clone) 内核配置 (menuconfig) 编译 (make Image dtbs modules) Image (内核镜像) *.dtb (设备树) *.ko (内核模块) 打包 (mkbootimg → boot.img) 烧录到板子

3.6 常见问题与排查

编译过程中,你可能会遇到各种报错。我整理了几个高频问题:

问题 原因 解决方法
arm-linux-gnueabihf-gcc: not found 交叉编译工具链未安装或未加入 PATH 安装 Linaro GCC,或检查 export PATH
Kernel panic - not syncing: VFS 根文件系统未找到或设备树错误 检查 .dtb 是否匹配,ramdisk 是否正确
make[1]: *** No rule to make target 'Image' 架构未指定或配置未生成 先执行 make rockchip_linux_defconfig
DTC: dts->dtb on file "xxx.dts" 报错 设备树语法错误 检查 DTS 文件中的括号、分号、标签

我的建议:第一次编译时,把 build.log 保存下来。出错了先看最后 50 行,大部分错误信息都在那里。别从头翻到尾,那是浪费时间。

3.7 小结

这一章我们走完了内核编译的完整流程:从 git clone 拉源码,到 menuconfig 配置,再到 make 编译,最后用 mkbootimg 打包成可烧录的镜像。每一步我都给了你实际项目中的经验和坑。

你可能会觉得步骤有点多,但多做几次就熟了。内核编译这件事,说白了就是「配置-编译-打包」三个环节,每个环节都有对应的工具和产物。只要理解了它们之间的关系,后面写驱动、调试的时候,你就知道该改哪里、编哪里、烧哪里了。