[第2课时 · 3/8] Kernel 交叉编译流程

0. 这一节要解决什么

编译内核的机器是 x86_64 的 PC(或虚拟机),运行内核的板子是 ARM64——CPU 指令集完全不同,PC 上的 gcc 编出的程序板子根本跑不了。本节回答:怎样让 x86 主机产出 ARM64 的内核,以及手工 make 与 SDK 脚本两种方式的关系。

1. 三方关系

编译主机 (x86_64)  ──调用──→  交叉工具链  ──输出──→  目标板 (ARM64)
运行 make / 脚本        aarch64-linux-gnu-gcc       加载并执行 Image
                       在主机上运行,产出 ARM64 指令

交叉编译不是「在 PC 上编译然后拷过去」那么简单——编译器本身决定生成哪种指令集。普通 gcc 生成 x86_64 指令;交叉工具链前缀为 aarch64-linux-gnu- 的 gcc 生成 ARM64 指令。

2. 两个参数各管一件事

参数 作用 错误后果
ARCH=arm64 选择目标架构:决定使用 arch/arm64/ 下的代码与 defconfig 找不到 defconfig,或编出 x86 内核
CROSS_COMPILE=aarch64-linux-gnu- 指定工具链前缀:make 会调用 aarch64-linux-gnu-gcc 等 报 aarch64-linux-gnu-gcc: No such file,或用了主机 gcc

记忆法:ARCH 管「用哪套架构的源码」,CROSS_COMPILE 管「用哪套编译器」。

3. 工具链从哪里来

两种来源:

  1. 系统安装:sudo apt install gcc-aarch64-linux-gnu(版本可能偏旧)
  2. SDK 自带:在 prebuilts/ 目录下,版本与 SDK 匹配

用 SDK 入口(build.sh)编译时,脚本会自动把工具链路径加入环境,不需要手工设置 CROSS_COMPILE;这也是推荐用 SDK 入口的原因之一。

4. 两种方式

手工方式——理解原理用,在 kernel/ 目录内执行:

cd kernel
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip_linux_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig          # 可选
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image dtbs

产物在 arch/arm64/boot/Image 与 arch/arm64/boot/dts/rockchip/*.dtb。

SDK 方式——课程实际使用,在 SDK 根目录执行:

./build.sh lunch          # 选择 firefly_rk3576_roc-rk3576-pc_ubuntu_defconfig
./build.sh extboot        # 编译内核并生成 boot 分区镜像 kernel/extboot.img

extboot 目标会完成「配置内核 → 编译 → 打包 boot 分区镜像」一整段,产物在 kernel/extboot.img 与 kernel/extboot/ 目录。

5. 增量编译与清理

  • 增量:直接重跑 ./build.sh extboot,Kbuild 只重编改动过的文件,第二次通常只需几十秒
  • 清理内核:./build.sh clean kernel;全量清理 ./build.sh cleanall
  • 何时必须 clean:切换 defconfig / 工具链 / 板级配置之后,残留的 .o 与 .config 可能导致产物错乱——出现诡异错误时,先 clean 再编是标准动作
  • 并行度:-j$(nproc) 用满所有核;内存小于 8G 时适当调小,避免 OOM

6. 常见报错对照

报错 原因 处理
aarch64-linux-gnu-gcc: No such file or directory 工具链不在 PATH 改用 SDK 入口,或给 CROSS_COMPILE 配绝对路径
arch/arm64/configs/xxx: No such file ARCH 未设或 defconfig 名不对 核对 ARCH=arm64 与配置文件名
编译到一半 Killed / cc1: out of memory 并行度过高,内存耗尽 减小 -j 值
产物时间戳是旧的、改动没生效 增量编译未识别 / 残留状态 ./build.sh clean kernel 后重编

7. Lab:两种方式各跑一遍,记录证据

# 1) 手工编一个 dtb(快,适合验证工具链)
cd kernel
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip_linux_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rk3576-firefly-roc-rk3576-pc.dtb
ls -l arch/arm64/boot/dts/rockchip/rk3576-firefly-roc-rk3576-pc.dtb

# 2) SDK 全流程
cd <SDK>
./build.sh lunch          # 选 firefly_rk3576_roc-rk3576-pc_ubuntu_defconfig
time ./build.sh extboot   # 记录耗时
ls -l kernel/extboot.img kernel/extboot/

要交的证据:两条产物的 ls -l 输出(含时间戳)与 time 耗时。

8. 本节小结

  • 交叉编译的三方:x86 主机跑工具,aarch64- 工具链产 ARM64 指令,板子只负责运行
  • ARCH 选架构源码,CROSS_COMPILE 选编译器;SDK 入口会自动备好两者
  • 手工 make 用于理解原理;课程与实际开发用 ./build.sh extboot
  • 增量编译快;切配置后先 clean;报错先对表

9. 本节思考

Q1:为什么不能直接在板子上编译内核?

查看答案

技术上可行(板子上有 gcc 就能自编译),但实际不可取:内核全量编译需要数 GB 内存与大量 CPU 时间,RK3576 的算力与内存远不如 x86 主机,编译一次可能要数小时甚至 OOM。交叉编译把「重计算」放在主机、「轻运行」放在板子,是嵌入式开发的标准分工。

Q2:手工 make 时漏掉 ARCH=arm64,会发生什么?为什么?

查看答案

make 按主机架构(x86_64)处理:会去 arch/x86/ 找 defconfig(报 No such file),即使运气好编下去,生成的也是 x86_64 内核,板子无法执行。因为内核构建系统用 ARCH 决定架构目录、头文件与指令集相关的全部选择,默认值就是当前主机架构。

Q3:./build.sh extboot 比「手工 make Image dtbs」多做的是什么?

查看答案

多了「准备板级配置 + 打包」两段:按板级入口设置 defconfig 与配置片段生成 .config、选定 RK_KERNEL_DTS_NAME 指定的 DTS,并把 Image、DTB 与 extlinux 配置打包成 boot 分区镜像 kernel/extboot.img。SDK 脚本是便捷入口,不是另一套编译系统——底层仍是 Kbuild 在干活。