第2章 嵌入式Linux开发环境搭建:交叉编译工具链安装、TFTP/NFS网络启动配置、Ubuntu开发环境初始化
说实话,很多初学者在嵌入式Linux上栽跟头,不是栽在代码逻辑上,而是栽在开发环境上。我见过太多人花了一周时间调一个驱动,最后发现是交叉编译工具链版本不对。所以这一章,咱们把地基打牢。
嵌入式Linux开发,说白了就是「在PC上写代码,在板子上跑代码」。PC是x86架构,板子是ARM架构,这就需要一个「翻译官」——交叉编译工具链。再加上TFTP和NFS这两个网络启动利器,你的开发效率能翻好几倍。
2.1 Ubuntu开发环境初始化
我个人习惯用Ubuntu 20.04 LTS做开发,稳定,软件源也全。如果你用22.04也没问题,但要注意Python和GCC的版本变化。
装好系统后,第一件事是换源。别用默认源,慢得让人抓狂。我一般用清华或中科大的镜像站。
# 备份原源
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
# 替换为清华源(Ubuntu 20.04)
sudo sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list
sudo sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list
# 更新
sudo apt update && sudo apt upgrade -y
接下来安装基础开发包。这里有个坑——千万别一股脑装所有包,按需安装才是正道。
# 基础开发工具
sudo apt install -y build-essential git vim curl wget \
net-tools openssh-server \
python3 python3-pip \
libncurses5-dev u-boot-tools \
device-tree-compiler
ssh-keygen 生成密钥,然后 ssh-copy-id 到板子。
2.2 交叉编译工具链安装
交叉编译工具链,说白了就是一套能在x86上生成ARM可执行文件的工具集。你想想看,你的工控网关是ARM芯片,但你的PC是x86,没有这个「翻译官」,你写的代码板子根本跑不起来。
市面上工具链很多,我推荐两种方案:
| 方案 | 适用场景 | 安装方式 | 我的评价 |
|---|---|---|---|
| Linaro GCC | ARM 32位/64位通用开发 | apt直接安装或下载预编译包 | 稳定,社区活跃,适合初学者 |
| ARM GCC (官方) | 裸机或RTOS开发 | 从ARM官网下载 | 更新快,但配置稍复杂 |
| Buildroot / Yocto | 定制化根文件系统 | 源码编译 | 功能强大,但编译耗时 |
咱们这门课主要针对工控网关,我建议用Linaro的GCC。原因很简单——apt直接装,省事。我在项目中用过很多次,没出过兼容性问题。
2.2.1 安装ARM 32位工具链(针对Cortex-A7/A8等)
# 安装ARM 32位交叉编译工具链
sudo apt install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
# 验证安装
arm-linux-gnueabihf-gcc --version
2.2.2 安装ARM 64位工具链(针对Cortex-A53/A72等)
# 安装ARM 64位交叉编译工具链
sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# 验证安装
aarch64-linux-gnu-gcc --version
uname -a 看板子信息,或者查芯片手册。
2.2.3 编写第一个交叉编译测试程序
装好工具链后,咱们写个Hello World验证一下。这一步不能省,我见过太多人装完工具链就以为万事大吉,结果编译出来的程序在板子上跑不了。
# 创建测试文件
cat > hello.c << 'EOF'
#include <stdio.h>
int main(void)
{
printf("Hello from ARM Gateway!\n");
printf("Cross-compilation works!\n");
return 0;
}
EOF
# 交叉编译
arm-linux-gnueabihf-gcc -o hello_arm hello.c
# 查看生成的文件信息
file hello_arm
# 输出应该包含: ELF 32-bit LSB executable, ARM, version 1 (SYSV)
看到 ARM 字样,说明编译成功了。把这个文件传到板子上跑一下:
# 通过scp传到板子(假设板子IP为192.168.1.100)
scp hello_arm root@192.168.1.100:/root/
# 在板子上执行
ssh root@192.168.1.100 "./hello_arm"
Hello from ARM Gateway!
Cross-compilation works!
看到这个输出,你的交叉编译环境就彻底通了。
2.3 TFTP网络启动配置
TFTP,简单说就是一个轻量级的文件传输协议。在嵌入式开发中,我们用它来通过网络把内核镜像和设备树文件传给U-Boot。为什么不用U盘?你想想看,每次编译完内核都要插拔U盘,效率太低了。网络传输,几秒钟搞定。
2.3.1 安装和配置TFTP服务器
# 安装TFTP服务器
sudo apt install -y tftpd-hpa
# 创建TFTP根目录
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp
# 修改配置文件
sudo vim /etc/default/tftpd-hpa
配置文件内容如下:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure --create"
注意 --create 这个选项,我一开始没加,结果板子往TFTP服务器写文件时一直报错。加上之后,板子就能通过TFTP把日志文件传回主机了。
# 重启服务
sudo systemctl restart tftpd-hpa
sudo systemctl enable tftpd-hpa
# 验证服务状态
sudo systemctl status tftpd-hpa
2.3.2 测试TFTP功能
# 在TFTP目录下放一个测试文件
echo "TFTP test file" | sudo tee /srv/tftp/test.txt
# 在本地测试(安装客户端)
sudo apt install -y tftp-hpa
tftp localhost
tftp> get test.txt
tftp> quit
# 查看下载的文件
cat test.txt
/srv/tftp 下按项目建子目录,比如 /srv/tftp/gateway_v1/,里面放内核、设备树、U-Boot等文件。这样多个项目不会搞混。
2.4 NFS网络启动配置
NFS(网络文件系统)是嵌入式开发的另一个利器。它允许板子通过网络挂载主机上的目录作为根文件系统。这意味着什么?你在主机上修改代码、编译,板子立刻就能用上新程序,不用烧写、不用重启。
我当年做第一个工控项目时,没有用NFS,每次改完驱动都要重新烧写根文件系统,一天下来光等烧写就花了两三个小时。后来用了NFS,开发效率至少提升了三倍。
2.4.1 安装和配置NFS服务器
# 安装NFS服务器
sudo apt install -y nfs-kernel-server
# 创建NFS共享目录
sudo mkdir -p /srv/nfs/rootfs
sudo chmod 777 /srv/nfs/rootfs
# 配置导出文件
sudo vim /etc/exports
在 /etc/exports 中添加:
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
这里解释一下几个关键参数:
- rw:读写权限,板子可以往共享目录写文件
- sync:同步写入,保证数据一致性
- no_root_squash:允许板子上的root用户拥有主机root权限。这个很重要,否则板子上的进程可能无法访问某些文件
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
# 验证导出
showmount -e localhost
2.4.2 准备根文件系统
你可以用Buildroot或Debootstrap生成根文件系统,也可以直接从板子厂商提供的镜像中提取。这里我演示用Debootstrap生成一个最小根文件系统:
# 安装Debootstrap
sudo apt install -y debootstrap
# 生成ARM 32位根文件系统
sudo debootstrap --arch=armhf --foreign bullseye /srv/nfs/rootfs
# 复制QEMU用户态模拟器(用于在主机上chroot)
sudo cp /usr/bin/qemu-arm-static /srv/nfs/rootfs/usr/bin/
# 完成安装
sudo chroot /srv/nfs/rootfs /debootstrap/debootstrap --second-stage
--arch=armhf 对应32位ARM,如果是64位板子,用 arm64。
2.5 验证网络启动环境
环境搭好了,咱们来验证一下。在U-Boot中设置以下环境变量:
# 设置IP地址(根据你的网络环境修改)
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10
setenv gatewayip 192.168.1.1
setenv netmask 255.255.255.0
# 设置TFTP启动参数
setenv bootcmd 'tftp 0x80800000 zImage; tftp 0x83000000 imx6ull.dtb; bootz 0x80800000 - 0x83000000'
# 设置NFS根文件系统参数
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.10:/srv/nfs/rootfs,proto=tcp rw ip=192.168.1.100'
# 保存并启动
saveenv
boot
如果一切正常,你会看到内核启动,然后挂载NFS根文件系统,最后出现登录提示符。
- ✅ 交叉编译工具链能生成ARM可执行文件
- ✅ TFTP服务器能正常传输文件
- ✅ NFS服务器能正常挂载
- ✅ 板子能通过网络启动内核并挂载根文件系统
2.6 常见问题与避坑指南
做嵌入式开发,环境问题占了调试时间的一半。下面是我这些年踩过的坑:
NFS: Unable to mount root fs。查了两天,最后发现是防火墙没关。Ubuntu默认开了ufw,把NFS端口(2049)给拦了。解决方案:sudo ufw disable 或者放行NFS端口。
其他常见问题:
- TFTP超时: 检查板子和主机的网络连通性,用
ping测试。另外,有些交换机端口开启了STP,会导致TFTP超时,可以尝试直连。 - NFS权限问题: 如果板子上报
Permission denied,检查/etc/exports中是否加了no_root_squash。 - 工具链版本不匹配: 内核编译用的工具链版本和应用程序用的工具链版本最好一致,否则可能出现
FATAL: kernel too old错误。