1. Linux 内核驱动开发环境搭建

嵌入式 Linux 驱动开发的第一步是构建一个完整、可复现的编译环境。与普通应用程序不同,内核模块必须与目标内核的源码树和编译配置严格对齐。本章将依次完成交叉编译工具链配置、内核源码树构建以及模块编译基础三个核心环节。

1.1 交叉编译工具链配置

交叉编译工具链运行在宿主机(x86_64 PC)上,生成目标板(ARM64 / ARM32)的可执行代码。工具链的选择直接影响内核模块的 ABI 兼容性。

组件 说明 典型包名(ARM64)
gcc C 编译器,生成目标架构指令 gcc-aarch64-linux-gnu
binutils 汇编器、链接器、objdump 等 binutils-aarch64-linux-gnu
glibc / uclibc C 运行时库(内核模块不直接链接,但影响头文件路径) libc6-dev-arm64-cross
kernel headers 目标内核的 asm 和 linux 头文件 linux-libc-dev-arm64-cross
推荐做法:使用 Linaro 或 ARM 官方预编译工具链,避免自行从源码构建。安装后确认 aarch64-linux-gnu-gcc --version 输出正常。

环境变量设置

将工具链路径加入 PATH,并导出交叉编译前缀:

export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64
export PATH=/opt/toolchains/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH
常见错误:忘记设置 ARCH 或 CROSS_COMPILE 会导致内核构建系统使用宿主机 gcc,编译出的模块无法在目标板加载。

1.2 内核源码树构建

内核模块编译需要完整的内核源码树,且必须包含已配置的 .config 和编译生成的 Module.symvers。推荐使用与目标板完全相同的内核版本。

获取源码

# 从 kernel.org 下载
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.28.tar.xz
tar -xf linux-6.1.28.tar.xz
cd linux-6.1.28

配置内核

使用目标板厂商提供的 defconfig,或从目标板运行中的 /proc/config.gz 提取:

# 使用 defconfig(以 ARM64 为例)
make ARCH=arm64 defconfig

# 或从目标板复制
zcat /proc/config.gz > .config
make ARCH=arm64 olddefconfig

编译内核与模块导出

至少需要编译一次完整内核,以生成 vmlinux、System.map 和 Module.symvers:

make -j$(nproc) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
make -j$(nproc) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules_prepare
关键文件清单:
  • ./.config — 内核配置选项
  • ./Module.symvers — 导出的符号 CRC 校验值
  • ./include/generated/autoconf.h — C 头文件形式的配置宏
  • ./arch/arm64/include/ — 架构相关头文件

1.3 模块编译基础

内核模块使用 kbuild 系统编译,通过 Makefile 描述源文件和编译选项。以下是一个最小模块示例。

模块源码:hello.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init hello_init(void)
{
    pr_info("Hello, Embedded Linux Driver!\n");
    return 0;
}

static void __exit hello_exit(void)
{
    pr_info("Goodbye, Embedded Linux Driver!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Driver Expert");
MODULE_DESCRIPTION("A minimal kernel module example");

Makefile

obj-m := hello.o

KDIR := /home/user/linux-6.1.28
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) ARCH=arm64 \
		CROSS_COMPILE=aarch64-linux-gnu- modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) ARCH=arm64 \
		CROSS_COMPILE=aarch64-linux-gnu- clean

编译与部署

make
# 生成 hello.ko

# 复制到目标板
scp hello.ko root@192.168.1.100:/root/

# 在目标板加载/卸载
insmod hello.ko
rmmod hello.ko
dmesg | tail -5
避坑提示:目标板内核版本与源码树版本必须完全一致,否则 insmod 会报 Invalid module format 或 Unknown symbol 错误。

1.4 环境搭建流程总览

环境搭建流程总览 ① 交叉工具链 gcc + binutils + headers ② 内核源码树 下载 → 配置 → 编译 ③ 模块编译 Makefile → kbuild → .ko 工具链子步骤 • 下载预编译包 • 解压到 /opt/ • 导出 PATH / ARCH 内核子步骤 • 获取源码(git/tar) • make defconfig • make modules_prepare 模块子步骤 • 编写 hello.c • 编写 Makefile • make → scp → insmod ✅ 验证:dmesg 查看输出 ⚠️ 常见错误 • “Invalid module format” → 内核版本/配置不匹配 • “Unknown symbol” → 缺少 Module.symvers 或符号未导出

1.5 常见问题与排查

现象 根因 解决方案
cc1: error: unknown target CPU 工具链与 ARCH 不匹配 检查 CROSS_COMPILE 前缀是否正确
make: *** No rule to make target 'modules_prepare' 内核源码未完整解压或路径错误 确认 KDIR 指向已配置的内源码根目录
insmod: ERROR: could not insert module 内核版本或 .config 不一致 在目标板运行 uname -r 对比源码版本
modprobe: FATAL: Module not found 模块路径未在 /lib/modules/ 下 使用 insmod /full/path/hello.ko 或执行 depmod
调试技巧:在 Makefile 中添加 V=1 可输出完整编译命令,便于定位头文件路径或链接错误:make V=1。