第三章:Linux内核编译——从源码到烧录的完整闭环
说实话,内核编译这件事,很多刚入行的朋友觉得挺神秘的。好像敲几个命令,等个十几分钟,就能得到一个能跑的内核。其实没那么玄乎,但也没那么简单。我做了这么多年嵌入式,内核编译翻车的次数,两只手都数不过来。今天我就把这块掰开了讲,从源码获取到最终烧录,每一步该注意什么,坑在哪里,一次性说清楚。
3.1 内核源码获取:别下错了版本
获取内核源码,第一件事就是确认你用的芯片型号。瑞芯微的芯片,比如RK3568、RK3588,它们对应的内核分支是不一样的。我见过有人拿着RK3399的内核去编译RK3568,结果折腾了两天没跑起来——嗯,这种低级错误其实挺常见的。
官方推荐的方式是从瑞芯微的GitHub仓库拉取:
git clone https://github.com/rockchip-linux/kernel.git -b rk356x-4.19
这里有个细节:-b参数指定分支。不同芯片对应不同分支,比如:
| 芯片型号 | 推荐内核分支 |
|---|---|
| RK3566/RK3568 | rk356x-4.19 |
| RK3588 | rk3588-5.10 |
| RK3399 | rk3399-4.19 |
拉完代码后,建议先看看arch/arm64/boot/dts/rockchip/目录下有没有你板子的设备树文件。如果没有,说明分支选错了,或者你需要自己写设备树——那是后面章节的内容了。
3.2 内核配置与裁剪:少即是多
内核配置,说白了就是决定「哪些功能要编译进内核,哪些不要」。默认配置通常很臃肿,把能开的都开了。但嵌入式设备资源有限,你想想看,一个只有256MB Flash的设备,你塞进去一个500MB的内核,那不是搞笑吗?
瑞芯微官方通常会提供一个defconfig文件,比如:
make ARCH=arm64 rockchip_linux_defconfig
这个文件是官方针对某类板子的基础配置。但注意,它不一定完全适合你的板子。我个人的习惯是:先用defconfig编译一次,确保能跑起来,然后再逐步裁剪。
3.2.1 裁剪的核心思路
裁剪不是乱删。你得知道你的板子上有哪些硬件。比如:
- 没有WiFi模块,就把无线网络驱动全关掉
- 没有蓝牙,就把蓝牙协议栈去掉
- 只用ext4文件系统,就把btrfs、xfs、f2fs全关了
进入配置菜单:
make ARCH=arm64 menuconfig
这里我分享一个技巧:用/键可以搜索配置项。比如你想找CONFIG_USB_NET_DRIVERS,直接按/输入关键词,回车就能定位。不用一个个菜单翻,效率高很多。
3.2.2 模块与静态编译的选择
内核有两种编译方式:
- 静态编译(built-in):直接编进内核镜像,启动时自动加载
- 模块(module):编译成.ko文件,需要时手动或自动加载
我的建议是:启动必需的驱动用静态编译,非必需的用模块。比如存储控制器、根文件系统驱动必须静态编译,否则内核都起不来。而像USB摄像头驱动、音频编解码器这些,用模块就行,省空间。
3.3 编译:耐心是美德
配置好了,接下来就是编译。命令很简单:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4
这里-j4表示用4个线程并行编译。如果你的电脑是8核,可以改成-j8。但注意,不是线程越多越好。我试过-j32,结果内存爆了,编译直接崩溃。一般来说,线程数不超过CPU物理核心数的1.5倍比较稳妥。
编译完成后,你会得到两个关键文件:
arch/arm64/boot/Image—— 内核镜像arch/arm64/boot/dts/rockchip/你的板子.dtb—— 设备树文件
make ARCH=arm64 dtbs。我见过有人改了dts文件,然后只编译了内核,没编译dtb,结果设备树还是旧的,折腾半天找不到原因。
3.4 烧录:最后一步,也是最容易翻车的一步
编译好了,怎么烧到板子上?瑞芯微的烧录方式主要有两种:
3.4.1 使用RKDevTool烧录
这是官方提供的Windows工具。操作步骤:
- 板子进入Loader模式(按住RECOVERY键,上电)
- 打开RKDevTool,选择对应的分区
- 把
boot.img(包含内核和设备树)烧到Boot分区 - 点击「执行」开始烧录
这里要注意:boot.img不是直接拿Image和dtb拼起来的。你需要用mkbootimg工具把它们打包:
mkbootimg --kernel arch/arm64/boot/Image \
--dtb arch/arm64/boot/dts/rockchip/你的板子.dtb \
--base 0x00200000 \
-o boot.img
--base参数的值取决于你的芯片。RK3568是0x00200000,RK3588是0x00200000,但RK3399是0x00280000。搞错了内核起不来。我建议你从官方SDK里找一个能用的boot.img,用dumpimage工具查看它的参数,然后照着来。
3.4.2 使用fastboot烧录
如果你的板子已经跑起来了,可以用fastboot:
fastboot flash boot boot.img
fastboot reboot
这种方式更快,适合开发阶段频繁调试。但前提是你的bootloader支持fastboot。瑞芯微的U-Boot默认是支持的,但有些第三方板子可能没开。
3.5 验证:跑起来才算数
烧录完成后,上电看串口日志。如果看到:
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x412fd050]
...
说明内核已经开始启动了。接着看有没有挂载根文件系统,有没有启动init进程。如果卡在某一步,比如:
[ 1.234567] VFS: Cannot open root device "mmcblk0p2"
说明内核找不到根文件系统。这时候检查两件事:一是设备树里mmc节点的配置对不对,二是内核有没有编译对应的存储驱动。
bootargs加上init=/bin/sh,这样内核会直接启动一个shell,方便排查问题。我调试新板子时经常用这招。
3.6 避坑指南
最后,我总结几个我踩过的坑,你遇到了可以少走弯路:
- 内核版本与工具链不匹配:4.19内核用gcc 7.x,5.10内核用gcc 9.x。版本差太多编译会报错。我试过用gcc 11编译4.19内核,结果一堆语法错误。
- 设备树编译警告不能忽略:编译dtb时如果有警告,比如
Warning (unit_address_vs_reg),最好修掉。有些警告会导致设备树解析失败,外设不工作。 - 烧录后没反应,先检查串口线:别笑,我至少有三次以为内核挂了,结果发现是串口线松了。先确认硬件连接,再怀疑软件。
好了,这一章的内容就到这里。内核编译说难不难,说简单也不简单。关键是每一步都要理解它在做什么,而不是机械地敲命令。下一章我们会讲设备树,那是嵌入式Linux开发的另一个核心。到时候见。