第2章 嵌入式Linux开发环境搭建:交叉编译工具链安装、TFTP/NFS网络启动配置、Ubuntu开发环境初始化

说实话,很多初学者在嵌入式Linux上栽跟头,不是栽在代码逻辑上,而是栽在开发环境上。我见过太多人花了一周时间调一个驱动,最后发现是交叉编译工具链版本不对。所以这一章,咱们把地基打牢。

嵌入式Linux开发,说白了就是「在PC上写代码,在板子上跑代码」。PC是x86架构,板子是ARM架构,这就需要一个「翻译官」——交叉编译工具链。再加上TFTP和NFS这两个网络启动利器,你的开发效率能翻好几倍。

本章核心目标: 在Ubuntu 20.04/22.04上搭建一套完整的嵌入式Linux开发环境,包括交叉编译工具链、TFTP服务器、NFS服务器,并验证环境可用。
嵌入式Linux开发环境架构图 Ubuntu 开发主机 (x86) 交叉编译工具链 arm-linux-gnueabihf-gcc / aarch64-linux-gnu-gcc TFTP 服务器 传输内核/设备树 NFS 服务器 共享根文件系统 串口终端 + 以太网 ARM 目标板 (工控网关) U-Boot Bootloader 通过TFTP加载内核 Linux 内核 通过NFS挂载根文件系统 根文件系统 (NFS共享) 应用程序、库文件、脚本 开发流程:PC端编写代码 → 交叉编译 → 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免密登录。后面你要频繁在主机和板子之间传文件、执行命令,每次输密码会疯掉。用 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
注意: 我曾经在一个项目里,因为用了32位工具链去编译64位内核,结果U-Boot死活起不来。折腾了两天才发现是工具链选错了。所以一定要确认你的板子CPU架构: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根文件系统,最后出现登录提示符。

验证清单:
  1. ✅ 交叉编译工具链能生成ARM可执行文件
  2. ✅ TFTP服务器能正常传输文件
  3. ✅ NFS服务器能正常挂载
  4. ✅ 板子能通过网络启动内核并挂载根文件系统
全部通过,你的开发环境就搭建完成了。

2.6 常见问题与避坑指南

做嵌入式开发,环境问题占了调试时间的一半。下面是我这些年踩过的坑:

我曾经... 在配置NFS时,板子一直报 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 错误。
我的建议: 把整个开发环境配置过程写成脚本,一键部署。这样换电脑、重装系统时,几分钟就能恢复环境。我自己的脚本已经用了五年,迭代了十几个版本,现在团队里新人都用这个脚本初始化环境。