2. 开发环境搭建:交叉编译工具链安装、内核源码树配置、最小系统构建与NFS/TFTP启动
嵌入式Linux驱动开发与桌面开发最大的区别在于交叉编译与目标板启动。本章将带你从零搭建一套可用的开发环境,涵盖工具链安装、内核源码树配置、最小根文件系统构建,以及通过NFS/TFTP实现网络启动。完成本章后,你将拥有一个可编译、可调试、可快速迭代的驱动开发实验平台。
环境概览: 主机(x86_64 Ubuntu 22.04) + 目标板(ARM Cortex-A7/A9,如STM32MP157或i.MX6ULL)。所有操作均在主机侧完成,目标板仅作为运行载体。
2.1 交叉编译工具链安装
交叉编译工具链是嵌入式开发的基石。它运行在x86主机上,生成ARM架构的可执行文件。推荐使用Linaro GCC或芯片厂商提供的预编译工具链。
2.1.1 下载与解压
以ARM Cortex-A7为例,下载gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf版本:
# 创建工具链目录
mkdir -p ~/toolchain && cd ~/toolchain
# 下载(若网络受限,可提前下载后拷贝)
wget https://releases.linaro.org/components/toolchain/binaries/9.2-2019.12/arm-linux-gnueabihf/gcc-arm-9.2-2019.12-x86_64-arm-linux-gnueabihf.tar.xz
# 解压
tar -xvf gcc-arm-9.2-2019.12-x86_64-arm-linux-gnueabihf.tar.xz
2.1.2 环境变量配置
将工具链的bin目录加入PATH,并设置交叉编译前缀:
# 编辑 ~/.bashrc,追加以下内容
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=$PATH:~/toolchain/gcc-arm-9.2-2019.12-x86_64-arm-linux-gnueabihf/bin
# 使配置生效
source ~/.bashrc
# 验证安装
arm-linux-gnueabihf-gcc --version
验证输出示例: arm-linux-gnueabihf-gcc (GCC) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599]
2.1.3 工具链组件说明
| 组件 | 用途 | 示例命令 |
|---|---|---|
| 编译器 | 将C/C++源码编译为ARM目标文件 | arm-linux-gnueabihf-gcc |
| 汇编器 | 将汇编代码转为机器码 | arm-linux-gnueabihf-as |
| 链接器 | 将多个目标文件链接为可执行文件 | arm-linux-gnueabihf-ld |
| 调试器 | 远程调试目标板上的程序 | arm-linux-gnueabihf-gdb |
| 二进制工具 | 查看/操作ELF、反汇编等 | arm-linux-gnueabihf-objdump |
2.2 内核源码树配置
驱动开发需要内核源码树提供头文件、Kconfig结构和编译框架。我们使用与目标板匹配的Linux内核版本(如Linux 5.10或6.1 LTS)。
2.2.1 获取内核源码
# 从内核官网或芯片厂商仓库克隆
git clone --depth=1 -b linux-5.10.y https://github.com/beagleboard/linux.git ~/kernel/linux-5.10
cd ~/kernel/linux-5.10
2.2.2 配置内核
使用芯片厂商提供的默认配置(defconfig)作为起点:
# 以STM32MP157为例
make ARCH=arm stm32mp1_defconfig
# 若需自定义,使用menuconfig图形界面
make ARCH=arm menuconfig
注意: 驱动开发阶段,建议开启以下选项:
CONFIG_MODULES=y —— 支持动态加载驱动模块CONFIG_DEBUG_FS=y —— 调试文件系统CONFIG_DYNAMIC_DEBUG=y —— 动态调试支持
2.2.3 编译内核与设备树
# 设置环境变量
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 编译内核镜像(zImage)
make -j$(nproc) zImage
# 编译设备树文件(.dtb)
make -j$(nproc) dtbs
# 编译内核模块
make -j$(nproc) modules
编译产物位于:
arch/arm/boot/zImage—— 内核镜像arch/arm/boot/dts/stm32mp157*.dtb—— 设备树二进制drivers/下各目录中的.ko文件 —— 内核模块
2.3 最小系统构建
一个可启动的嵌入式Linux系统包含三个核心组件:Bootloader(如U-Boot)、内核镜像(zImage)、根文件系统(rootfs)。本节聚焦于构建最小根文件系统。
2.3.1 使用BusyBox构建最小rootfs
BusyBox是一个集成了数百个常用Unix命令的单一可执行文件,非常适合嵌入式环境。
# 下载BusyBox
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
# 配置为静态编译(避免依赖动态库)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 进入 Settings → Build static binary (no shared libs) → 选中
# 编译并安装到指定目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- CONFIG_PREFIX=~/rootfs install
2.3.2 完善根文件系统目录结构
cd ~/rootfs
mkdir -p proc sys dev tmp etc/init.d lib/modules
# 创建必要的设备节点
sudo mknod -m 666 dev/console c 5 1
sudo mknod -m 666 dev/null c 1 3
# 创建初始化脚本
cat > etc/init.d/rcS << 'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /tmp
echo "Welcome to Embedded Linux!"
EOF
chmod +x etc/init.d/rcS
# 创建inittab(BusyBox init使用)
cat > etc/inittab << 'EOF'
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
EOF
最小rootfs大小: 使用静态编译的BusyBox,整个rootfs仅约2~3MB,非常适合NFS启动或烧录到SPI Flash。
2.4 NFS/TFTP启动配置
在驱动开发阶段,频繁烧写Flash效率极低。使用NFS挂载根文件系统 + TFTP下载内核,可实现“主机编译 → 目标板秒级启动”的快速迭代流程。
2.4.1 主机端配置NFS服务器
# 安装NFS服务器
sudo apt install nfs-kernel-server
# 编辑 /etc/exports,添加共享目录
echo "/home/$(whoami)/rootfs *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
# 验证挂载
showmount -e localhost
2.4.2 主机端配置TFTP服务器
# 安装TFTP服务器
sudo apt install tftpd-hpa
# 配置TFTP根目录(存放zImage和dtb)
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp
sudo cp ~/kernel/linux-5.10/arch/arm/boot/zImage /srv/tftp/
sudo cp ~/kernel/linux-5.10/arch/arm/boot/dts/stm32mp157*.dtb /srv/tftp/
# 编辑 /etc/default/tftpd-hpa,确保以下配置
# TFTP_DIRECTORY="/srv/tftp"
# TFTP_OPTIONS="--secure --create"
# 重启TFTP服务
sudo systemctl restart tftpd-hpa
2.4.3 U-Boot启动参数设置
在目标板的U-Boot命令行中设置以下环境变量:
# 设置网络参数(根据实际网络修改)
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10
setenv netmask 255.255.255.0
# 设置内核启动参数(关键!)
setenv bootargs 'console=ttySTM0,115200 root=/dev/nfs nfsroot=192.168.1.10:/home/yourname/rootfs,proto=tcp rw ip=192.168.1.100:192.168.1.10:192.168.1.1:255.255.255.0::eth0:off'
# 从TFTP下载内核和设备树并启动
setenv bootcmd 'tftp 0xc2000000 zImage; tftp 0xc4000000 stm32mp157.dtb; bootz 0xc2000000 - 0xc4000000'
# 保存并启动
saveenv
boot
2.5 环境验证与常见问题
2.5.1 验证清单
| 验证项 | 命令/操作 | 预期结果 |
|---|---|---|
| 工具链可用 | arm-linux-gnueabihf-gcc -v |
显示版本信息,无错误 |
| 内核可编译 | make zImage |
生成 arch/arm/boot/zImage |
| NFS共享正常 | showmount -e |
显示 rootfs 路径 |
| TFTP可下载 | tftp localhost -c get zImage |
成功传输文件 |
| 目标板网络连通 | U-Boot下 ping 192.168.1.10 |
收到应答 |
2.5.2 常见问题与解决
问题1:NFS挂载失败,提示 "VFS: Unable to mount root fs"
原因:内核未开启NFS客户端支持。
解决:在menuconfig中启用
原因:内核未开启NFS客户端支持。
解决:在menuconfig中启用
CONFIG_NFS_FS=y 和 CONFIG_IP_PNP=y,重新编译内核。
问题2:TFTP下载超时
原因:防火墙未放行TFTP端口(69/UDP)。
解决:
原因:防火墙未放行TFTP端口(69/UDP)。
解决:
sudo ufw allow 69/udp 或临时关闭防火墙测试。
问题3:BusyBox启动后提示 "can't run /etc/init.d/rcS: Permission denied"
原因:rcS脚本没有可执行权限。
解决:
原因:rcS脚本没有可执行权限。
解决:
chmod +x ~/rootfs/etc/init.d/rcS,并确认NFS共享的no_root_squash选项已设置。
2.6 本章小结
至此,你已经完成了嵌入式Linux驱动开发环境的完整搭建:
- 交叉编译工具链 —— 在x86主机上编译ARM代码
- 内核源码树 —— 配置、编译内核与设备树
- 最小根文件系统 —— 使用BusyBox构建轻量rootfs
- 网络启动 —— NFS挂载rootfs + TFTP下载内核,实现秒级启动迭代