2. 开发环境搭建:交叉编译工具链安装、Linux内核源码获取与解压、内核配置与编译、最小根文件系统制作

嵌入式Linux驱动开发与普通PC开发最大的区别在于交叉编译。目标板(ARM、RISC-V等)资源有限,无法直接运行编译工具链,因此需要在性能强大的宿主机(x86_64)上安装交叉编译工具链,生成目标板可执行的二进制文件。本章将完整演示从零搭建开发环境的四个核心步骤。

2.1 交叉编译工具链安装

交叉编译工具链通常由芯片厂商(如NXP、TI、Rockchip)或社区(Linaro、Buildroot)提供。以ARM Cortex-A7目标板为例,推荐使用gcc-arm-linux-gnueabihf工具链。

组件 说明 典型版本
gcc 交叉编译器 10.3-2021.07
binutils 汇编器、链接器 2.36.1
glibc C运行库(目标板架构) 2.33
linux-headers 内核头文件(用于编译系统调用) 5.10
安装路径建议:统一放在 /opt/toolchains/ 目录下,便于多版本管理和环境变量配置。

安装步骤:

  1. 从Linaro官网或ARM官方下载预编译工具链压缩包(.tar.xz)。
  2. 解压到指定目录:sudo tar -xvf gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf.tar.xz -C /opt/toolchains/
  3. 添加环境变量到 ~/.bashrc:
export PATH=$PATH:/opt/toolchains/gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf/bin
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
  1. 验证安装:arm-linux-gnueabihf-gcc --version,输出应显示版本号及目标架构信息。
常见错误:如果提示“No such file or directory”,通常是因为宿主机缺少32位兼容库(如 lib32z1、lib32ncurses5),执行 sudo apt install lib32z1 lib32ncurses5 即可解决。

2.2 Linux内核源码获取与解压

驱动开发必须拥有与目标板匹配的内核源码。获取方式有三种:

  • 官方主线:从 kernel.org 下载长期支持(LTS)版本,如5.10.y、6.1.y。
  • 芯片厂商BSP:如NXP的Linux BSP、TI的Processor SDK,包含板级配置和驱动补丁。
  • SoC厂商Git仓库:如Rockchip、Allwinner在GitHub上维护的分支。

解压与目录结构:

tar -xvf linux-5.10.120.tar.xz -C ~/workspace/
cd ~/workspace/linux-5.10.120
ls -l arch/arm/   # ARM架构相关代码
ls -l drivers/    # 所有设备驱动源码
ls -l include/    # 内核头文件

内核源码根目录的关键子目录:

目录 内容 驱动开发者关注点
arch/ 体系结构相关代码(arm、arm64、riscv等) 板级文件、DTS、内存布局
drivers/ 所有设备驱动 字符设备、平台设备、I2C、SPI等
include/linux/ 内核公共头文件 device.h、fs.h、cdev.h等
Documentation/ 内核文档(含设备树绑定) 驱动API使用说明

2.3 内核配置与编译

内核配置是驱动开发中最关键的环节之一。配置错误会导致驱动无法编译或内核无法启动。

配置流程:

  1. 加载默认配置:针对特定开发板,如 make vexpress_defconfig(ARM Versatile Express平台)。
  2. 自定义配置:执行 make menuconfig,进入图形化配置界面。
  3. 关键配置项:
    • Device Drivers → Character devices:确保字符设备框架开启。
    • General setup → Local version:添加自定义版本后缀,便于区分。
    • Kernel Features → Preemption Model:根据实时性需求选择。
  4. 保存配置:退出menuconfig时选择“Save”,配置写入 .config 文件。

编译命令:

make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage
make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
make -j$(nproc) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules

编译产物说明:

产物 路径 用途
zImage arch/arm/boot/zImage 压缩后的内核镜像,烧录到目标板
dtbs arch/arm/boot/dts/*.dtb 设备树二进制文件,描述硬件拓扑
modules 各驱动目录下的.ko文件 可动态加载的内核模块
性能提示:使用 -j$(nproc) 并行编译,可大幅缩短编译时间。首次全量编译约需10-30分钟,后续增量编译仅需几十秒。
内核配置与编译流程 获取内核源码 make xxx_defconfig make menuconfig make zImage make dtbs make modules 提示:menuconfig 修改后自动保存到 .config,增量编译只重新编译修改部分

2.4 最小根文件系统制作

根文件系统(RootFS)是Linux启动后挂载的第一个文件系统,包含init程序、C库、设备节点和必要的工具。驱动调试阶段不需要完整的桌面环境,最小根文件系统即可满足需求。

制作方法对比:

方法 体积 复杂度 适用场景
BusyBox ~2MB 低 驱动开发初期、资源受限设备
Buildroot ~8MB 中 需要定制化工具包
Yocto ~50MB+ 高 产品级完整发行版

使用BusyBox制作最小根文件系统(推荐):

  1. 下载BusyBox源码:wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
  2. 解压并配置:make menuconfig,选择 Build static binary (no shared libs) 避免动态库依赖。
  3. 编译安装:make -j$(nproc) && make install CONFIG_PREFIX=~/rootfs
  4. 创建必要目录:
cd ~/rootfs
mkdir -p proc sys dev etc/init.d lib/modules
  1. 创建 etc/inittab 和 etc/init.d/rcS 启动脚本:
# etc/inittab
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot

# etc/init.d/rcS
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mdev -s
echo "Welcome to Minimal RootFS"
  1. 赋予执行权限:chmod +x etc/init.d/rcS
  2. 复制交叉编译的C库到 lib/ 目录(如果BusyBox未静态编译):
cp -a /opt/toolchains/gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/* lib/
快速验证:使用QEMU模拟器测试根文件系统:qemu-system-arm -M vexpress-a9 -kernel zImage -dtb vexpress-v2p-ca9.dtb -sd rootfs.ext2 -append "root=/dev/mmcblk0 console=ttyAMA0"
最小根文件系统目录结构 / (rootfs) bin/ sbin/ etc/ lib/ dev/ proc/ init.d/ modules/ sysfs 和 tmpfs 在启动时由 init 脚本挂载,无需预先创建

2.5 环境验证清单

完成上述步骤后,建议执行以下验证,确保环境可用:

  • 工具链验证:编译一个简单的Hello World程序,通过 file 命令确认输出为ARM架构ELF。
  • 内核验证:检查 arch/arm/boot/zImage 是否存在,大小是否合理(通常3-8MB)。
  • 根文件系统验证:使用QEMU或直接烧录到SD卡,观察串口输出是否正常进入shell。
  • 模块编译验证:编写一个空的 hello.ko 模块,执行 make -C $(KERNEL_DIR) M=$(PWD) modules,确认生成 .ko 文件。
开发环境搭建是驱动开发的基础,建议将工具链、内核源码、根文件系统三者版本对齐。推荐使用同一厂商BSP包中的配套版本,避免因API差异导致驱动编译失败。