3. 开发环境搭建:安装交叉编译工具链、配置TFTP/NFS服务、搭建最小化实验环境
说实话,嵌入式开发环境搭建这件事,看着简单,但坑是真不少。我见过太多新手,甚至有些老手,卡在环境配置上好几天。说白了,这步要是没弄好,后面写再多代码都是白搭。
这一章,我就带你一步步把环境搭起来。咱们的目标很明确:装好交叉编译工具链、配好TFTP和NFS服务、跑通最小化实验环境。这三样东西,就是你后面所有实验的基石。
本章核心目标:在Ubuntu 20.04/22.04宿主机上,搭建一个完整的嵌入式开发环境,能编译、能下载、能调试。
3.1 交叉编译工具链的安装
先说说什么是交叉编译。你想想看,你的开发机是x86架构,但目标板是ARM架构。x86上编译出来的程序,ARM板子能直接跑吗?当然不能。所以我们需要一个交叉编译工具链——在x86上运行,但生成ARM机器码的编译器。
我个人习惯用Linaro提供的工具链,稳定、社区活跃、更新及时。当然,你也可以用ARM官方的gcc-arm-none-eabi,或者自己用crosstool-NG编译一个。但说实话,对于学习Busybox来说,Linaro的预编译版本就足够了。
3.1.1 下载与解压
我建议你统一把工具链放在 /opt/toolchains 目录下,方便管理。别问我为什么,我在项目里吃过乱放的亏——后来换电脑、换项目,找工具链找得想哭。
# 创建工具链目录
sudo mkdir -p /opt/toolchains
cd /opt/toolchains
# 下载Linaro ARM交叉编译工具链(以gcc-arm-10.2-2020.11为例)
sudo wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 解压
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 重命名(方便后续版本切换)
sudo mv gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf arm-linux-gnueabihf-7.5
小提示:如果你用的是ARM64架构的板子(比如树莓派3B+、RK3399),记得下载aarch64版本的工具链。别搞混了,我见过有人拿arm-linux-gnueabihf去编译aarch64的内核,结果折腾了一整天。
3.1.2 配置环境变量
解压完只是第一步,你得让系统能找到这些工具。配置环境变量,我推荐写在 ~/.bashrc 里,而不是 /etc/profile。为什么?因为每个用户可能有不同的工具链需求,全局配置容易打架。
# 编辑 ~/.bashrc
vim ~/.bashrc
# 在文件末尾添加
export PATH=$PATH:/opt/toolchains/arm-linux-gnueabihf-7.5/bin
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
# 使配置生效
source ~/.bashrc
配置完,验证一下:
# 检查编译器版本
arm-linux-gnueabihf-gcc --version
# 如果看到类似下面的输出,说明安装成功
# arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0
注意:如果你同时装了多个版本的GCC,记得检查PATH顺序。系统会使用第一个找到的版本。我曾经因为PATH顺序问题,用错了编译器版本,编译出来的内核在板子上跑起来就崩,查了三天才找到原因。
3.2 TFTP服务配置
TFTP(Trivial File Transfer Protocol)说白了就是一个超简单的文件传输协议。在嵌入式开发中,我们常用它来通过网络下载内核镜像和设备树文件到开发板。为什么不用FTP或SCP?因为TFTP足够轻量,而且U-Boot原生支持。
3.2.1 安装与配置
在Ubuntu上安装TFTP服务很简单:
# 安装tftp-hpa(客户端)和tftpd-hpa(服务端)
sudo apt-get update
sudo apt-get install tftp-hpa tftpd-hpa -y
安装完,需要配置一下:
# 编辑配置文件
sudo vim /etc/default/tftpd-hpa
# 修改为以下内容
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp" # TFTP根目录
TFTP_ADDRESS="0.0.0.0:69" # 监听所有IP的69端口
TFTP_OPTIONS="-l -c -s" # -l: 日志, -c: 允许创建文件, -s: 安全模式
创建TFTP根目录并设置权限:
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp
sudo chown tftp:tftp /srv/tftp
# 重启服务
sudo systemctl restart tftpd-hpa
sudo systemctl enable tftpd-hpa
3.2.2 测试TFTP服务
配置完,一定要测试一下。我习惯用本地回环地址测试:
# 在TFTP目录下创建一个测试文件
echo "Hello TFTP" | sudo tee /srv/tftp/test.txt
# 用tftp客户端测试
tftp localhost
tftp> get test.txt
tftp> quit
# 检查是否下载成功
cat test.txt
如果能看到 "Hello TFTP",说明服务正常。
避坑指南:防火墙可能会拦截TFTP的69端口。如果你发现客户端连不上,先检查防火墙:sudo ufw status。如果开启了,记得放行:sudo ufw allow 69/udp。
3.3 NFS服务配置
NFS(Network File System)比TFTP强大得多。它允许开发板直接挂载宿主机上的目录,就像访问本地硬盘一样。这对于调试阶段特别有用——你可以在宿主机上编译程序,开发板立刻就能运行,不用反复烧写。
3.3.1 安装与配置
# 安装NFS服务端
sudo apt-get install nfs-kernel-server -y
配置共享目录:
# 创建NFS共享目录
sudo mkdir -p /srv/nfs/rootfs
# 编辑exports文件
sudo vim /etc/exports
# 添加以下内容(假设开发板IP为192.168.1.0/24网段)
/srv/nfs/rootfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
参数说明:
| 参数 | 含义 |
|---|---|
| rw | 允许读写 |
| sync | 同步写入,保证数据一致性 |
| no_subtree_check | 禁用子树检查,提升性能 |
| no_root_squash | 允许开发板上的root用户拥有宿主机root权限 |
重要:no_root_squash 这个参数要谨慎使用。它意味着开发板上的root用户对共享目录有完全控制权。在调试阶段没问题,但生产环境千万别这么配。我见过有人忘了改这个,结果开发板被入侵后,宿主机也跟着遭殃。
配置完,重启NFS服务:
sudo systemctl restart nfs-kernel-server
sudo systemctl enable nfs-kernel-server
# 验证共享是否生效
showmount -e localhost
3.4 搭建最小化实验环境
好了,工具链和服务都配好了。现在我们来搭建一个最小化的实验环境。这个环境包括:一个能启动的Linux内核、一个Busybox构建的根文件系统、以及通过TFTP/NFS启动的配置。
3.4.1 整体架构
先看看整个实验环境的架构,我画了一张图帮你理解:
3.4.2 准备内核和根文件系统
先编译一个最小化的内核。这里我以Linux 5.10 LTS版本为例:
# 下载内核源码
cd ~
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xvf linux-5.10.100.tar.xz
cd linux-5.10.100
# 配置内核(使用默认的vexpress配置,适合QEMU模拟)
make vexpress_defconfig
# 编译内核(-j4表示用4个线程编译,根据你的CPU核心数调整)
make -j4 zImage dtbs
# 编译完成后,复制到TFTP目录
cp arch/arm/boot/zImage /srv/tftp/
cp arch/arm/boot/dts/vexpress-v2p-ca9.dtb /srv/tftp/
接下来,用Busybox构建根文件系统。这部分我会在后面的章节详细讲,这里先搭个最小框架:
# 下载Busybox
cd ~
wget https://busybox.net/downloads/busybox-1.33.2.tar.bz2
tar -xvf busybox-1.33.2.tar.bz2
cd busybox-1.33.2
# 配置Busybox
make defconfig
make menuconfig # 这里可以按需裁剪,后面章节会细讲
# 编译并安装到NFS目录
make -j4
make CONFIG_PREFIX=/srv/nfs/rootfs install
3.4.3 配置U-Boot启动参数
最后,在U-Boot中设置启动参数。假设你的开发板IP为192.168.1.100,宿主机IP为192.168.1.10:
# 在U-Boot命令行中设置
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10
setenv netmask 255.255.255.0
# 设置TFTP下载内核和设备树
setenv bootcmd 'tftp 0x60000000 zImage; tftp 0x65000000 vexpress-v2p-ca9.dtb; bootz 0x60000000 - 0x65000000'
# 设置内核启动参数(NFS根文件系统)
setenv bootargs 'console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.1.10:/srv/nfs/rootfs rw ip=192.168.1.100'
# 保存并启动
saveenv
boot
调试技巧:如果启动失败,别慌。先检查网络连通性:在U-Boot里用 ping 192.168.1.10 测试。如果ping不通,八成是网线或IP配置问题。我遇到过最奇葩的一次,是交换机端口被配置了VLAN隔离,折腾了两小时才发现。
3.5 验证环境
环境搭好了,怎么验证?我的做法是:写一个简单的Hello World程序,交叉编译后放到NFS目录,在开发板上运行。
# 在宿主机上
cat > hello.c << EOF
#include <stdio.h>
int main()
{
printf("Hello from ARM!\n");
printf("Busybox environment is ready!\n");
return 0;
}
EOF
# 交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c
# 复制到NFS目录
cp hello /srv/nfs/rootfs/
然后在开发板的串口终端上:
# 在开发板上
cd /
./hello
# 如果看到输出
# Hello from ARM!
# Busybox environment is ready!
看到这个输出,说明你的整个开发环境已经跑通了。交叉编译、TFTP下载、NFS挂载、Busybox根文件系统,所有环节都正常工作。
环境验证清单:
- ✅ 交叉编译工具链能正常编译ARM程序
- ✅ TFTP服务能下载内核和设备树
- ✅ NFS服务能挂载根文件系统
- ✅ 开发板能通过U-Boot引导内核
- ✅ Busybox shell能正常执行程序
嗯,到这里,你的嵌入式开发环境就搭建完成了。后面所有的实验,都会基于这个环境进行。记住,环境搭建是基础中的基础,花点时间把它搞扎实,后面会省很多事。