Linux内核调度器移植适配到ARM64平台实践工程 - 第4节

4. Linux内核编译与构建系统

Linux 内核的配置与编译系统由 Kconfig 和 Makefile 两套语言协同驱动。Kconfig 定义配置选项的层次结构与依赖关系,Makefile 根据配置结果决定哪些源文件参与编译、如何链接。理解这套体系是移植调度器到 ARM64 平台的基础。

4.1.1 Kconfig 配置树与调度器相关选项

Kconfig 采用树形结构组织配置项,根节点为 arch/arm64/Kconfig,通过 source 指令逐级包含子目录的 Kconfig 文件。调度器相关的配置选项主要分布在以下路径:

路径 说明 关键选项示例
kernel/sched/Kconfig 调度器核心配置 SCHED_FIFO, SCHED_RR, SCHED_DEADLINE
kernel/sched/Kconfig.preempt 抢占模式选择 PREEMPT_VOLUNTARY, PREEMPT, PREEMPT_RT
arch/arm64/Kconfig ARM64 平台特性 ARM64_HAS_IRQ_PRIO_MASKING
drivers/cpufreq/Kconfig CPU 频率调节 CPUFREQ_DT (设备树驱动)
移植要点: 在 arch/arm64/Kconfig 中新增调度器特性时,必须使用 depends on 声明对 ARM64 架构的依赖,避免配置项在其他架构下意外可见。

4.1.2 Makefile 编译规则与目标文件生成

内核 Makefile 采用递归编译模型。顶层 Makefile 设置全局变量(如 ARCH、CROSS_COMPILE),然后进入各子目录执行编译。调度器核心代码位于 kernel/sched/,其 Makefile 片段如下:

# kernel/sched/Makefile
obj-y += core.o
obj-y += fair.o
obj-y += rt.o
obj-y += deadline.o
obj-$(CONFIG_SCHED_WALT) += walt.o
obj-$(CONFIG_SCHED_TUNE) += tune.o

编译流程的关键变量:

  • obj-y:无条件编译的目标文件
  • obj-$(CONFIG_XXX):根据配置选项决定是否编译
  • lib-y:编译为库文件,供其他模块链接
调试技巧: 在调度器 Makefile 中临时添加 CFLAGS_core.o += -DDEBUG_SCHED 可以开启单文件的调试打印,无需修改全局 KBUILD_CFLAGS。
Kconfig / Makefile 协同工作流程 arch/arm64/Kconfig kernel/sched/Kconfig .config 配置文件 顶层 Makefile kernel/sched/Makefile sched/core.o vmlinux / Image Kconfig 生成 .config → Makefile 读取 .config 决定编译对象 → 链接生成最终内核镜像

4.2 ARM64 交叉编译工具链配置

ARM64 平台通常无法本地编译内核(资源受限),必须使用交叉编译工具链。工具链的选择与配置直接影响调度器代码的编译优化与调试能力。

4.2.1 工具链选择标准

工具链 提供商 适用场景 调度器相关特性
aarch64-linux-gnu- Linaro / 发行版 通用内核编译 支持 -march=armv8-a 指令集
aarch64-linux-android- Android NDK Android 内核 支持 -fomit-frame-pointer 优化
clang + llvm LLVM 项目 调试 / 静态分析 AddressSanitizer 检测调度器内存错误

4.2.2 环境变量与 Makefile 参数

交叉编译时,必须显式指定架构和工具链前缀。以下为典型配置:

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export CC=${CROSS_COMPILE}gcc
export LD=${CROSS_COMPILE}ld
export OBJDUMP=${CROSS_COMPILE}objdump

在顶层 Makefile 中,这些变量控制编译器的调用方式:

# 顶层 Makefile 片段
AS      = $(CROSS_COMPILE)as
CC      = $(CROSS_COMPILE)gcc
LD      = $(CROSS_COMPILE)ld.bfd
CPP     = $(CC) -E
AR      = $(CROSS_COMPILE)ar
NM      = $(CROSS_COMPILE)nm
STRIP   = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
常见错误: 忘记设置 ARCH=arm64 会导致内核使用 x86 的 Kconfig 和 Makefile,编译出的镜像无法在 ARM64 硬件上启动。务必在 make 命令前检查环境变量。

4.2.3 调度器编译优化选项

针对 ARM64 调度器代码,推荐在 Makefile 或 KBUILD_CFLAGS 中添加以下优化:

  • -march=armv8-a+crc+crypto:启用硬件 CRC 和加密指令,加速调度器中的哈希计算
  • -mtune=cortex-a76:针对特定微架构优化指令调度
  • -fno-stack-protector:关闭栈保护(调试阶段),减少调度器热路径开销
  • -g:保留调试符号,便于使用 gdb 单步跟踪调度器代码
ARM64 交叉编译工具链配置流程 x86 主机 aarch64-gcc Linux 内核源码 Image / vmlinux 环境变量:ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- CC=clang

4.3 内核镜像生成流程

从源码到可启动的 ARM64 内核镜像,需要经过配置、编译、链接、打包四个阶段。调度器代码的修改会直接影响 kernel/sched/ 目录下的编译单元。

4.3.1 完整构建步骤

  1. 清理旧产物:make ARCH=arm64 distclean 或 make ARCH=arm64 mrproper
  2. 加载默认配置:make ARCH=arm64 defconfig 或 make ARCH=arm64 menuconfig 手动调整
  3. 编译内核:make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
  4. 编译设备树:make ARCH=arm64 dtbs
  5. 打包镜像:make ARCH=arm64 Image.gz 或 make ARCH=arm64 Image.lz4

4.3.2 镜像格式与生成路径

镜像文件 生成路径 说明 调度器相关
vmlinux 源码根目录 未压缩的 ELF 文件,含调试符号 可直接用 gdb 加载并设置调度器断点
Image arch/arm64/boot/ 纯二进制内核镜像,无压缩 QEMU 模拟器常用格式
Image.gz arch/arm64/boot/ gzip 压缩镜像,节省存储 启动时由 bootloader 解压
Image.lz4 arch/arm64/boot/ LZ4 压缩镜像,解压速度更快 适合调度器调试时频繁刷写

4.3.3 调度器代码修改后的增量编译

当只修改调度器源文件时,无需完整重编整个内核。利用 Makefile 的依赖追踪机制:

# 只编译 kernel/sched/ 目录
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- M=kernel/sched

# 查看具体编译命令(不执行)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- kernel/sched/core.o -n
性能提示: 使用 make -j$(nproc) 并行编译时,调度器代码的编译顺序由 Makefile 的依赖关系自动保证。如果修改了 include/linux/sched.h 头文件,所有包含该头文件的 .c 文件都会重新编译,建议使用 ccache 加速。
ARM64 内核镜像生成流程 内核源码 .config 编译 .o 文件 vmlinux Image.gz dtb 文件 关键命令速查 1. make ARCH=arm64 defconfig 2. make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8 3. make ARCH=arm64 Image.gz dtbs

4.4 本章小结

  • Kconfig 定义调度器配置选项的可见性与依赖,Makefile 根据配置决定编译单元
  • ARM64 交叉编译必须设置 ARCH=arm64 和 CROSS_COMPILE,工具链选择影响调度器优化效果
  • 内核镜像生成遵循 配置 → 编译 → 链接 → 压缩 的流水线,调度器代码修改后可使用增量编译加速迭代
  • 调试阶段优先使用 vmlinux(含符号),部署阶段使用 Image.gz 或 Image.lz4