2. Linux内核开发环境搭建:交叉编译工具链、内核源码获取与配置、QEMU模拟器与开发板环境
嵌入式Linux内核驱动开发与原生PC开发最大的区别在于目标架构不同。开发机(x86_64)与目标板(ARM/AArch64/RISC-V)的指令集不兼容,因此必须搭建一套完整的交叉编译环境,并配合模拟器或物理开发板进行验证。本章将系统讲解从零搭建这套环境的核心步骤与工具链选型。
2.1 交叉编译工具链
交叉编译工具链由编译器(gcc)、汇编器(as)、链接器(ld)、库(glibc/musl)及调试器(gdb)组成,针对目标架构独立构建。常用获取方式有三种:
| 方式 | 命令/工具 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 发行版包管理器 | apt install gcc-arm-linux-gnueabihf |
快速原型验证 | 安装简单,系统集成好 | 版本较旧,目标架构有限 |
| Linaro预编译工具链 | 从 releases.linaro.org 下载 |
ARMv7/ARMv8 主流开发 | 版本新,优化好,支持多库变体 | 需手动配置 PATH |
| crosstool-NG 自编译 | ct-ng build |
定制化/非主流架构 | 完全可控,可定制内核/库版本 | 编译耗时,学习曲线陡 |
| Buildroot/Yocto SDK | make sdk |
产品级项目 | 与根文件系统、内核版本严格对齐 | 构建时间长,体积大 |
安装并验证工具链:
# 下载并解压
wget https://releases.linaro.org/components/toolchain/binaries/10.3-2021.07/arm-linux-gnueabihf/gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf.tar.xz
tar -xf gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf.tar.xz -C /opt/
# 添加至 PATH
export PATH=/opt/gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf/bin:$PATH
# 验证
arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-objdump -V
arm-linux-gnueabihf/libc,编译内核模块时通常不需要额外指定,但编译用户态驱动测试程序时必须通过 --sysroot 显式指定。
2.2 内核源码获取与配置
Linux内核源码获取首选 kernel.org 的长期支持(LTS)版本。本课程使用 Linux 5.10 LTS,该版本对 ARM 嵌入式平台支持成熟,且 SPI 子系统接口稳定。
# 获取主线源码
git clone --depth 1 --branch v5.10 git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git linux-5.10
cd linux-5.10
# 或者直接下载 tarball
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.200.tar.xz
tar -xf linux-5.10.200.tar.xz
内核配置是环境搭建中最关键的环节。配置方法分为三个层级:
- 架构与板级默认配置:
make ARCH=arm vexpress_defconfig—— 生成针对 ARM Versatile Express 平台的基线配置。 - menuconfig 微调:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig—— 按需开启 SPI 子系统、传感器驱动、设备树支持等。 - 碎片配置(.config)保存:
make savedefconfig生成精简 defconfig,便于版本管理。
CONFIG_SPI=y —— SPI 子系统核心CONFIG_SPI_PL022=y —— ARM PrimeCell PL022 SPI 控制器驱动(vexpress 板载)CONFIG_OF=y —— 设备树支持CONFIG_SPI_SPIDEV=y —— SPI 用户态设备接口(调试用)
编译内核与设备树:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc) zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc) dtbs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc) modules
编译产物:arch/arm/boot/zImage(内核镜像)、arch/arm/boot/dts/vexpress-v2p-ca9.dtb(设备树二进制)。
2.3 QEMU模拟器环境
QEMU 支持全系统模拟(system mode),可在 x86_64 开发机上运行 ARM 内核与完整的根文件系统。本课程使用 vexpress-a9 开发板模型,该模型包含 PL022 SPI 控制器,完美适配 SPI 驱动实验。
安装 QEMU 并验证:
sudo apt install qemu-system-arm
qemu-system-arm --version
准备根文件系统(使用 Buildroot 生成最小 ext4 镜像):
# 使用预构建的最小根文件系统(包含 busybox、libc、SPI 测试工具)
wget https://example.com/rootfs.ext4 # 实际课程提供内部镜像
# 或使用 Buildroot 自行构建:
# git clone https://git.buildroot.net/buildroot
# cd buildroot; make qemu_arm_vexpress_defconfig; make
启动 QEMU 并挂载内核与设备树:
qemu-system-arm \
-M vexpress-a9 \
-kernel linux-5.10/arch/arm/boot/zImage \
-dtb linux-5.10/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-drive file=rootfs.ext4,if=sd,format=raw \
-append "console=ttyAMA0 root=/dev/mmcblk0 rw" \
-serial stdio \
-net nic -net user
-s(GDB stub 监听 tcp:1234)和 -S(启动时暂停等待调试器连接)。配合 arm-linux-gnueabihf-gdb vmlinux 可进行内核源码级调试,对 SPI 驱动中断处理、DMA 传输等场景的调试极为有效。
2.4 开发板环境(物理硬件)
当驱动需要在真实硬件上验证时序、电气特性或中断延迟时,必须使用物理开发板。本课程以 STM32MP157 和 Raspberry Pi 3B+ 作为参考平台,两者均具备 SPI 总线且文档丰富。
| 特性 | STM32MP157 (DK2) | Raspberry Pi 3B+ | vexpress-a9 (QEMU) |
|---|---|---|---|
| CPU 架构 | ARM Cortex-A7 (双核) | ARM Cortex-A53 (四核) | ARM Cortex-A9 (单核) |
| SPI 控制器 | STM32 SPI (5路) | BCM2837 SPI (2路) | PL022 (1路) |
| 调试接口 | ST-Link + UART | GPIO + UART | GDB stub |
| 内核加载方式 | TFTP / SD 卡 | SD 卡 | -kernel 参数 |
| 适用阶段 | 产品级验证 | 原型验证 | 开发调试 |
开发板环境搭建要点:
- 串口终端:使用
screen /dev/ttyUSB0 115200或minicom连接板载 UART,获取内核启动日志。 - 网络启动(TFTP/NFS):在开发机上配置 TFTP 服务器存放 zImage/dtb,NFS 导出根文件系统,开发板通过 Uboot 加载,实现快速迭代。
- SPI 物理连接:注意电平匹配(3.3V vs 1.8V)、片选信号极性、SPI 模式(CPOL/CPHA)与传感器数据手册严格对应。
2.5 环境验证:第一个 SPI 驱动模块
搭建完成后,编写一个最简单的 SPI 设备驱动模块,验证整个工具链与运行环境是否正常工作。
// hello_spi.c
#include <linux/module.h>
#include <linux/spi/spi.h>
static int __init hello_spi_init(void)
{
pr_info("Hello SPI: environment is ready!\n");
return 0;
}
static void __exit hello_spi_exit(void)
{
pr_info("Goodbye SPI\n");
}
module_init(hello_spi_init);
module_exit(hello_spi_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SPI environment verification module");
编译与测试:
# 在内核源码树外编译
make -C /path/to/linux-5.10 M=$(pwd) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
# 将 .ko 复制到根文件系统,启动 QEMU 后加载
insmod hello_spi.ko
dmesg | tail -5
# 预期输出: [ xxx] Hello SPI: environment is ready!
✅ 交叉编译工具链可生成 ARM 目标文件(
file hello_spi.ko 显示 ARM ELF)✅ 内核编译无错误,zImage 与 dtb 生成成功
✅ QEMU 启动后内核控制台输出完整,网络/存储设备识别正常
✅ SPI 控制器驱动已加载(
cat /sys/bus/spi/devices 显示 spi0.0 等节点)✅ 内核模块插入与卸载无异常