2. Linux内核源码获取与配置

内核模块开发的第一步是获取并正确配置 Linux 内核源码树。源码树不仅是模块编译所需的头文件与构建系统的来源,更是理解内核架构与驱动接口的基础。本节将系统讲解从源码获取、解压、配置到理解 Kconfig / Makefile 机制的完整流程。

2.1 从 kernel.org 获取内核源码

Linux 内核官方发布站点为 kernel.org。开发者应优先选择 长期支持(LTS) 版本用于嵌入式产品开发,以获得长期的安全更新与稳定 API。

版本类型 特点 适用场景
主线(Mainline) 最新特性,API 不稳定 开发测试、新硬件支持
稳定(Stable) 基于主线,修复关键 Bug 一般桌面/服务器
长期支持(LTS) 2~6 年维护周期,API 稳定 嵌入式产品、工业控制
推荐做法:嵌入式开发优先选择 LTS 版本(如 6.1.x、5.15.x),并记录当前使用的精确版本号,便于后续环境复现与补丁管理。

获取源码的典型命令如下:

# 通过 wget 下载 LTS 内核源码包
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.76.tar.xz

# 校验 SHA256 签名(强烈建议)
sha256sum linux-6.1.76.tar.xz

2.2 解压与目录结构

解压源码包后,应了解内核源码树的顶层目录布局,这对后续配置与模块开发至关重要。

# 解压到 /home/developer/kernel/
tar -xf linux-6.1.76.tar.xz -C /home/developer/kernel/
cd /home/developer/kernel/linux-6.1.76
关键目录速查表:
目录 内容说明 模块开发关注度
arch/ 体系结构相关代码(arm64、x86、riscv 等) 高(需匹配目标板架构)
drivers/ 设备驱动源码(char、net、i2c、spi 等) 极高(参考与复用)
include/ 内核头文件(linux/、asm-generic/、uapi/) 极高(模块编译依赖)
scripts/ 构建脚本、Kconfig 解析工具、模块签名工具 中
Documentation/ 内核文档(含驱动开发指南) 高(查阅 API 与规范)
Kbuild 顶层构建描述文件 中

2.3 内核配置:Kconfig 与 Makefile 机制

内核配置系统由 Kconfig 和 Makefile 协同工作。Kconfig 定义配置选项的菜单结构、依赖与类型;Makefile 根据配置结果决定哪些文件参与编译。

Kconfig 与 Makefile 协同工作流程 Kconfig 文件 定义配置选项、依赖、默认值 make menuconfig / defconfig 生成 .config 配置文件 .config 文件 CONFIG_XXX=y / m / n Makefile 文件 obj-$(CONFIG_XXX) += xxx.o make 编译 读取 .config,条件编译 vmlinux / .ko 模块 最终二进制产物 读取 核心规则 Kconfig 决定“有哪些选项”,Makefile 决定“编译哪些文件”,.config 是两者的桥梁

2.4 配置内核的常用方法

嵌入式开发中,最常用的配置命令如下:

  • make defconfig:使用当前架构的默认配置(适合快速起步)。
  • make menuconfig:基于 ncurses 的文本菜单界面,最常用。
  • make olddefconfig:基于现有 .config 更新,新选项使用默认值。
  • make savedefconfig:将当前 .config 精简为最小化 defconfig。
常见陷阱:直接使用 make defconfig 后,务必检查 CONFIG_LOCALVERSION 和 CONFIG_CROSS_COMPILE 是否匹配目标板。交叉编译时,必须显式指定 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-。

2.5 理解 Kconfig 语法要点

Kconfig 文件位于每个子目录中,采用树形结构组织。以下是一个典型的驱动 Kconfig 条目:

# drivers/char/my_drv/Kconfig
config MY_DRV
    tristate "My Custom Driver Support"
    depends on ARM64 || COMPILE_TEST
    select MISC_DEVICE
    help
      This is a sample driver for learning kernel module development.
      Say Y to build it into the kernel, M to build as a module.
关键字 含义 示例值
tristate 三态:Y(编入内核)/ M(模块)/ N(不编译) tristate "My Driver"
bool 二态:Y / N bool "Enable Feature"
depends on 依赖其他配置选项 depends on I2C
select 反向选择(自动启用另一选项) select MISC_DEVICE
help 帮助文本,menuconfig 中显示 help\n This driver ...

2.6 理解 Makefile 条件编译

内核 Makefile 使用 obj-$(CONFIG_XXX) 模式实现条件编译。当 CONFIG_XXX 为 y 时,目标文件编入内核;为 m 时,编译为独立模块(.ko)。

# drivers/char/my_drv/Makefile
obj-$(CONFIG_MY_DRV) += my_drv.o
my_drv-objs := main.o ioctl.o probe.o

# 如果依赖多个源文件,使用 <module_name>-objs 或 <module_name>-y
模块开发关键点:外部模块(out-of-tree)的 Makefile 需要引用内核构建系统,典型写法如下:
# 外部模块 Makefile 示例
obj-m := hello.o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

2.7 配置验证与 .config 检查

配置完成后,应检查关键选项是否生效:

# 检查模块是否被配置为模块(m)
grep CONFIG_MY_DRV .config

# 检查架构与交叉编译链
grep -E "CONFIG_ARM64|CROSS_COMPILE" .config

# 生成精简 defconfig 用于版本管理
make savedefconfig
cp defconfig arch/arm64/configs/my_board_defconfig
最佳实践:将最终使用的 .config 文件纳入版本控制(Git),并配合 scripts/diffconfig 工具追踪配置变更。这能有效避免“编译通过但运行异常”的配置漂移问题。