2. 内核编译流程
内核编译并非简单的 make 命令,而是一套由 Kconfig 与 Makefile 协同驱动的复杂构建系统。理解这一流程是进行高效裁剪与加速编译的前提。本章将拆解从配置到生成二进制镜像的完整链路。
2.1 Kconfig 与 Makefile 的交互机制
Kconfig 定义了内核功能的菜单树,而 Makefile 则根据配置结果决定哪些文件参与编译。两者通过 .config 文件中的 CONFIG_* 变量进行桥接。
核心交互流程: Kconfig 定义选项 → 用户通过
make menuconfig 选择 → 生成 .config → Makefile 读取 .config 中的 CONFIG_* 变量 → 条件编译对应源文件。
2.2 配置生成:从 Kconfig 到 .config
.config 是内核编译的唯一配置真相源。其生成过程遵循严格的依赖解析与冲突检测规则。
2.2.1 配置生成步骤
- 默认配置加载:
make defconfig或make ARCH=arm64 defconfig生成基于架构的默认配置。 - 交互式调整:
make menuconfig/make nconfig/make xconfig提供图形化界面修改。 - 依赖自动解析:Kconfig 的
depends on和select规则被自动满足,缺失依赖的选项会被强制关闭或自动选中。 - 冲突检测:
make olddefconfig自动将未定义的选项设为默认值,并解决符号冲突。 - 保存输出:最终写入
.config,同时生成include/config/auto.conf供 Makefile 快速读取。
性能优化提示: 使用
make savedefconfig 可生成最小化 defconfig 文件,仅包含非默认值的选项,便于版本管理。
2.2.2 .config 文件结构示例
# 三态选项:编译进内核 (y)、编译为模块 (m)、禁用 (n)
CONFIG_NET=y
CONFIG_IPV6=m
CONFIG_IPV6_SIT=m
# CONFIG_IPV6_TUNNEL is not set
# 整型与字符串选项
CONFIG_HZ=250
CONFIG_CMDLINE="console=ttyS0,115200"
# 依赖自动生成的选项
CONFIG_IPV6_SIT_6RD=y # 由 CONFIG_IPV6_SIT 自动 select
2.3 编译目标与模块编译
内核 Makefile 提供了丰富的编译目标,理解其分类有助于加速构建过程。
2.3.1 主要编译目标分类
| 目标 | 说明 | 典型耗时 |
|---|---|---|
all / vmlinux |
编译完整内核镜像(包含所有 =y 的代码) |
最长 |
modules |
仅编译标记为 =m 的内核模块 |
中等 |
modules_install |
将编译好的模块安装到 /lib/modules/$(uname -r)/ |
短 |
dtbs |
编译设备树二进制文件(ARM/ARM64/RISC-V 架构) | 短 |
clean / mrproper |
清理编译产物;mrproper 额外删除 .config |
极短 |
2.3.2 模块编译的两种模式
- 树内模块编译:
make M=fs/btrfs仅编译指定路径下的模块,依赖已构建好的内核树。 - 树外模块编译:
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules,用于驱动开发,依赖已安装的内核头文件。
2.4 编译流程中的关键文件与变量
| 文件/变量 | 作用 | 生成时机 |
|---|---|---|
.config |
用户配置的最终存储,Makefile 直接读取 | 每次 make *config 后 |
include/config/auto.conf |
.config 的预处理版本,供 Makefile 快速 include |
配置生成后自动同步 |
include/generated/autoconf.h |
C 语言头文件,将 CONFIG_* 转为 #define 宏 |
配置生成后自动生成 |
Module.symvers |
记录模块导出的符号及 CRC 校验值,用于模块间依赖 | 模块编译时生成 |
$(obj-y) |
Makefile 变量,包含所有 =y 的目标文件,链接入 vmlinux |
Makefile 解析时 |
$(obj-m) |
Makefile 变量,包含所有 =m 的目标文件,编译为 .ko |
Makefile 解析时 |
常见陷阱: 修改
.config 后必须执行 make olddefconfig 或 make menuconfig 并保存,否则 Makefile 不会识别手动编辑的配置。直接编辑 .config 而不更新 auto.conf 会导致编译使用旧配置。
2.5 编译流程的并行化与加速
内核编译天然支持并行,合理利用多核 CPU 可显著缩短编译时间。
- 并行编译:
make -j$(nproc)使用所有 CPU 核心。建议-j参数设为 CPU 核心数的 1.5~2 倍以掩盖 I/O 等待。 - 局部编译:
make drivers/net/仅编译网络驱动子系统,适合调试时快速验证。 - 编译缓存:使用
ccache可缓存编译中间结果,二次编译加速明显。 - LD 优化:使用
make LD=ld.lld启用 LLVM 的 lld 链接器,链接阶段比 GNU ld 快 2~5 倍。
# 典型加速编译命令组合
make -j$(nproc) CC=clang LD=ld.lld LLVM=1
# 仅编译修改过的模块
make -j$(nproc) M=drivers/net/ethernet/intel/igb