1. Linux 驱动开发环境搭建:交叉编译工具链安装、内核源码树配置、NFS与TFTP服务搭建
嵌入式Linux驱动开发与原生Linux开发最大的区别在于交叉编译。目标板(ARM、RISC-V等)资源有限,无法运行完整的编译工具链,因此需要在性能强劲的宿主机(Host)上编译驱动模块,再部署到目标板(Target)上运行。本章将系统讲解开发环境的三大核心支柱:交叉编译工具链、内核源码树、以及网络文件系统(NFS)与TFTP服务。
环境概览: 本课程以 Ubuntu 22.04 LTS 为宿主机,目标板为 ARM Cortex-A7(基于 STM32MP157 平台),内核版本 Linux 5.10。所有操作均在此组合下验证,其他平台可类比调整。
1.1 交叉编译工具链安装
交叉编译工具链是一套运行在宿主机上、但生成目标板机器码的编译器、链接器、调试器及库文件的集合。安装方式主要有三种:
| 安装方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 发行版包管理器 | 快速验证、标准平台 | 命令简单,依赖自动解决 | 版本可能较旧,非标准架构支持有限 |
| 芯片厂商SDK | 商业项目、特定芯片 | 针对性优化,包含板级支持包 | 体积大,安装步骤复杂 |
| 手动构建crosstool-NG | 定制化需求、学习研究 | 高度可定制,理解工具链内部 | 编译耗时,配置繁琐 |
本课程采用发行版包管理器方式,快速搭建ARM交叉编译环境:
# 安装ARM交叉编译工具链(gcc、g++、binutils等)
sudo apt update
sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
# 验证安装
arm-linux-gnueabihf-gcc --version
# 输出示例:arm-linux-gnueabihf-gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0
关键路径: 工具链安装后,可执行文件位于
/usr/bin/arm-linux-gnueabihf-*。驱动开发中常用 arm-linux-gnueabihf-objdump 反汇编模块,arm-linux-gnueabihf-objcopy 转换格式。
交叉编译工具链的命名规则为 arch-vendor-os-abi。以 arm-linux-gnueabihf 为例:
- arm:目标架构
- linux:操作系统
- gnueabihf:使用GNU C库,支持硬件浮点(Hard Float)
下面通过一张SVG图展示交叉编译与本地编译的流程对比:
1.2 内核源码树配置
驱动模块本质上是内核的一部分,编译时必须依赖内核源码树提供的头文件、Makefile规则和配置信息。因此,必须准备一份与目标板运行内核版本完全一致的内核源码,并完成配置和编译。
1.2.1 获取与解压内核源码
# 从内核官网或芯片厂商获取源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.160.tar.xz
tar -xf linux-5.10.160.tar.xz
cd linux-5.10.160
1.2.2 配置内核
配置内核有三种方式,推荐使用基于厂商默认配置的增量配置:
| 配置命令 | 说明 | 使用场景 |
|---|---|---|
make ARCH=arm defconfig |
使用架构默认配置 | 通用平台,快速开始 |
make ARCH=arm <board>_defconfig |
使用厂商提供的板级配置 | 特定开发板,最常用 |
make ARCH=arm menuconfig |
图形化菜单配置 | 手动调整内核功能开关 |
# 以STM32MP157为例,使用厂商默认配置
make ARCH=arm stm32mp1_defconfig
# 可选:进一步定制内核功能
make ARCH=arm menuconfig
常见错误: 编译驱动模块时出现
Cannot use CONFIG_CC_STACKPROTECTOR_STRONG 或 No rule to make target 'modules_prepare',通常是因为内核源码未完成 make modules_prepare 步骤。务必执行以下命令:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_prepare
1.2.3 编译内核与模块
# 编译内核镜像(耗时较长,可使用 -jN 并行编译)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
编译完成后,内核源码树根目录下会生成 vmlinux(未压缩内核)、arch/arm/boot/zImage(压缩内核镜像)以及 arch/arm/boot/dts/*.dtb(设备树二进制文件)。
下面通过SVG图展示内核源码树配置与驱动编译的关系:
1.3 NFS与TFTP服务搭建
驱动开发需要频繁将编译好的内核镜像、设备树、驱动模块传输到目标板并测试。NFS和TFTP是两种最常用的网络传输方式:
- TFTP(Trivial File Transfer Protocol):UDP协议,用于引导阶段加载内核镜像和设备树。Bootloader(如U-Boot)内置TFTP客户端。
- NFS(Network File System):TCP/IP协议,用于内核启动后挂载根文件系统,实现宿主机与目标板文件共享,方便模块热加载与调试。
1.3.1 搭建TFTP服务
# 安装TFTP服务器
sudo apt install tftpd-hpa
# 配置TFTP服务
sudo vim /etc/default/tftpd-hpa
# 修改为以下内容:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp" # TFTP根目录
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure --create"
# 创建TFTP根目录并设置权限
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp
# 重启服务
sudo systemctl restart tftpd-hpa
sudo systemctl enable tftpd-hpa
# 测试TFTP服务(本机回环测试)
echo "test" > /srv/tftp/test.txt
tftp localhost -c get test.txt
U-Boot中使用TFTP: 在目标板U-Boot命令行中执行:
setenv serverip 192.168.1.100 # 宿主机IP
setenv ipaddr 192.168.1.10 # 目标板IP
tftp 0xc2000000 zImage # 加载内核到内存
tftp 0xc4000000 stm32mp157.dtb # 加载设备树
bootz 0xc2000000 - 0xc4000000 # 启动内核
1.3.2 搭建NFS服务
# 安装NFS服务器
sudo apt install nfs-kernel-server
# 创建NFS共享目录(通常作为目标板的根文件系统)
sudo mkdir -p /srv/nfs/rootfs
# 配置NFS导出
sudo vim /etc/exports
# 添加以下行:
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
# 应用配置
sudo exportfs -a
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
# 验证NFS挂载(本机测试)
sudo mount -t nfs localhost:/srv/nfs/rootfs /mnt
ls /mnt
sudo umount /mnt
权限避坑:
no_root_squash 参数允许目标板以root身份访问共享目录,驱动模块加载和调试需要root权限。如果省略此参数,目标板上的root用户会被映射为nobody,导致insmod失败。
1.3.3 内核启动参数配置
目标板通过内核命令行参数指定NFS根文件系统。在U-Boot中设置:
setenv bootargs 'console=ttySTM0,115200 root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs,v3,tcp ip=192.168.1.10:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'
saveenv
boot
参数说明:
| 参数 | 含义 | 示例值 |
|---|---|---|
root=/dev/nfs |
使用NFS作为根文件系统 | 固定写法 |
nfsroot |
NFS服务器IP、共享路径、协议版本 | 192.168.1.100:/srv/nfs/rootfs,v3,tcp |
ip |
目标板IP:服务器IP:网关:子网掩码::网卡:自动配置 | 192.168.1.10:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off |
下面通过SVG图展示NFS与TFTP在驱动开发流程中的协作关系:
1.4 环境验证清单
完成上述搭建后,建议逐项验证环境可用性:
| 验证项 | 验证命令 | 预期结果 |
|---|---|---|
| 交叉编译器 | arm-linux-gnueabihf-gcc --version |
显示版本号,无错误 |
| 内核配置 | ls .config |
文件存在,非空 |
| 内核编译 | ls arch/arm/boot/zImage |
文件存在 |
| TFTP服务 | tftp localhost -c get test.txt |
成功下载文件 |
| NFS服务 | showmount -e localhost |
显示共享目录列表 |
开发效率建议: 将以下环境变量写入
~/.bashrc,避免每次编译重复输入:
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export KERNEL_DIR=/home/user/linux-5.10.160
然后执行 source ~/.bashrc 生效。