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。
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单步跟踪调度器代码
4.3 内核镜像生成流程
从源码到可启动的 ARM64 内核镜像,需要经过配置、编译、链接、打包四个阶段。调度器代码的修改会直接影响 kernel/sched/ 目录下的编译单元。
4.3.1 完整构建步骤
- 清理旧产物:
make ARCH=arm64 distclean或make ARCH=arm64 mrproper - 加载默认配置:
make ARCH=arm64 defconfig或make ARCH=arm64 menuconfig手动调整 - 编译内核:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) - 编译设备树:
make ARCH=arm64 dtbs - 打包镜像:
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 加速。
4.4 本章小结
- Kconfig 定义调度器配置选项的可见性与依赖,Makefile 根据配置决定编译单元
- ARM64 交叉编译必须设置
ARCH=arm64和CROSS_COMPILE,工具链选择影响调度器优化效果 - 内核镜像生成遵循 配置 → 编译 → 链接 → 压缩 的流水线,调度器代码修改后可使用增量编译加速迭代
- 调试阶段优先使用
vmlinux(含符号),部署阶段使用Image.gz或Image.lz4