4、编译系统入门:SDK一键编译脚本解析,单独编译Uboot、Kernel、Rootfs,输出产物说明。
说实话,刚拿到Rockchip SDK的时候,我第一反应是——这玩意儿怎么这么复杂?
一堆脚本,一堆目录,不知道从哪下手。后来做多了才发现,其实核心就三个东西:Uboot、Kernel、Rootfs。搞懂它们怎么编译,整个SDK就通了一半。
核心认知:Rockchip SDK 的编译系统本质是一个“脚本套脚本”的洋葱结构。最外层是 build.sh,它帮你把 Uboot、Kernel、Rootfs 的编译命令串起来。你不需要记住每个细节,但必须知道每个环节产出了什么。
4.1 SDK 一键编译脚本解析
先看最常用的命令:
./build.sh lunch # 选择板型
./build.sh uboot # 编译 Uboot
./build.sh kernel # 编译 Kernel
./build.sh rootfs # 编译 Rootfs
./build.sh all # 一键全编译
这个 build.sh 其实是个“调度员”。它读取 device/rockchip/ 下的板级配置,然后调用各个子模块的编译脚本。我个人习惯先跑 ./build.sh lunch,确认板型选对了,再分步编译。为什么?因为全编译出错了,你根本不知道是哪个环节挂了。
来看看 build.sh 的核心逻辑:
# 简化后的 build.sh 流程
function build_uboot() {
cd u-boot
make ${UBOOT_DEFCONFIG}
make -j$(nproc)
cd -
}
function build_kernel() {
cd kernel
make ${KERNEL_DEFCONFIG}
make -j$(nproc) Image dtbs
cd -
}
function build_rootfs() {
cd buildroot
make ${ROOTFS_DEFCONFIG}
make -j$(nproc)
cd -
}
嗯,这里要注意:build.sh 里每个函数都独立执行,互不依赖。这意味着你可以单独编译 Uboot,然后单独编译 Kernel,最后单独打包 Rootfs。我在项目中遇到过好几次这种情况——Kernel 改了驱动,但 Uboot 和 Rootfs 完全不用动,重新编译 Kernel 就行。
我的习惯:第一次编译用 ./build.sh all 打通流程,之后只编译改动的模块。这样每次能省 5-10 分钟。
4.2 单独编译 Uboot
Uboot 是第一个上电的程序。它负责初始化硬件、加载 Kernel 到内存。
单独编译 Uboot 的命令:
cd u-boot
make rk3399_defconfig # 以 RK3399 为例
make -j8
# 产物在 u-boot/ 目录下
编译完成后,你会看到这些文件:
| 产物文件 | 说明 | 烧录位置 |
|---|---|---|
uboot.img |
Uboot 主镜像,包含 SPL 和 Uboot 本体 | SD 卡/ eMMC 的 0x4000 扇区 |
trust.img |
ATF(ARM Trusted Firmware)镜像 | 0x6000 扇区 |
rk3399_loader_v1.xxx.bin |
一级 loader,芯片出厂固件 | 0x0 扇区 |
idbloader.img |
SPL + DDR 初始化代码 | 0x2000 扇区 |
我曾经踩过一个坑:只更新了 uboot.img,没更新 trust.img,结果板子启动到一半就卡住了。后来才发现 ATF 和 Uboot 版本要匹配。所以我的建议是——Uboot 编译完,把 uboot.img 和 trust.img 一起烧录。
4.3 单独编译 Kernel
Kernel 是操作系统的核心。Rockchip 的 Kernel 编译比 Uboot 稍微复杂一点,因为要生成多个文件。
cd kernel
make ARCH=arm64 rockchip_linux_defconfig
make ARCH=arm64 -j8 Image dtbs modules
# 产物在 kernel/arch/arm64/boot/ 下
编译完你会得到:
| 产物文件 | 说明 | 用途 |
|---|---|---|
Image |
压缩后的内核镜像 | Uboot 加载到内存并启动 |
*.dtb |
设备树二进制文件 | 描述硬件信息,Kernel 解析 |
*.ko |
内核模块 | 动态加载的驱动 |
这里有个细节:make dtbs 会编译所有设备树文件。但实际你只需要板子对应的那个 .dtb。比如 RK3399 的 EVB 板,就是 rk3399-evb.dtb。我一般会单独指定:
make ARCH=arm64 rk3399-evb.dtb
这样编译快很多,尤其是你只改了一个 dts 文件的时候。
注意:Kernel 的 .config 文件不要手动改。用 make menuconfig 或者 make savedefconfig 来管理。我见过有人直接改 .config,结果下次 make clean 后配置全丢了。
4.4 单独编译 Rootfs
Rootfs 是根文件系统。Rockchip SDK 默认用 Buildroot 来生成。编译命令:
cd buildroot
make rockchip_rk3399_defconfig
make -j8
# 产物在 buildroot/output/images/ 下
编译完你会看到:
| 产物文件 | 说明 | 大小参考 |
|---|---|---|
rootfs.ext4 |
ext4 格式的根文件系统镜像 | 约 256MB ~ 1GB |
rootfs.squashfs |
只读压缩文件系统 | 约 50MB ~ 200MB |
rootfs.cpio.gz |
initramfs 格式 | 约 10MB ~ 50MB |
你想想看,为什么有这么多格式?
- ext4:可读写,适合开发调试。我平时都用这个。
- squashfs:只读,适合量产。体积小,安全性高。
- cpio.gz:内存文件系统,适合紧急启动或 recovery 模式。
我个人习惯在开发阶段用 rootfs.ext4,因为可以随时挂载修改。量产时再切到 rootfs.squashfs。
4.5 输出产物总览与烧录关系
把所有产物放在一起看,烧录顺序是这样的:
这张图把整个流程串起来了。你从左边开始,依次编译每个模块,最后烧录到右边的 eMMC 或 SD 卡。每个箭头代表一个独立的编译动作。
我的建议:第一次接触时,先跑一遍 ./build.sh all,确认环境没问题。然后分别单独编译 Uboot、Kernel、Rootfs,看看每个环节的产物。这样你对整个系统就有了“手感”。
4.6 常见问题与避坑
- 编译 Uboot 报错:检查交叉编译器版本。Rockchip 官方推荐 gcc 7.5 或 8.3。我用 gcc 9 也试过,但偶尔会出奇怪的问题。
- Kernel 编译后找不到 dtb:确认
ARCH环境变量设置正确。我经常忘记设ARCH=arm64,结果编译出 x86 的 dtb。 - Rootfs 编译特别慢:第一次编译 Buildroot 要下载很多包,建议用代理或者提前下载好源码包放到
dl/目录下。 - 烧录后板子不启动:先检查串口日志。如果卡在
DDR init,那就是 loader 或 trust 的问题。如果卡在Starting kernel,那就是 Kernel 或 dtb 的问题。
嗯,编译系统入门其实就这些。说白了,你只要记住三个独立模块、各自的产物、以及烧录顺序,剩下的就是多练。我在 RK3399、RK3568、RK3588 上都走过同样的流程,套路一模一样。