1. 课程导入

说实话,嵌入式Linux开发的第一步,也是最容易让人栽跟头的一步,就是搭环境。很多新手一上来就急着写代码,结果编译报错、下载失败、调试不了,折腾半天发现是环境没配好。我做了这么多年嵌入式,见过太多人卡在这一关。

这一章,咱们就把这三样东西搞定:交叉编译工具链、NFS网络文件系统、TFTP服务器。这三样是嵌入式Linux开发的"三驾马车",缺一不可。

核心思路:你的PC(x86架构)性能强、资源多,用来写代码和编译;目标板(ARM架构)资源有限,只负责运行。中间靠交叉编译工具链生成ARM能跑的二进制,靠NFS共享文件系统,靠TFTP传输内核和镜像。

嵌入式Linux开发环境架构图 嵌入式Linux开发环境架构图 开发主机(x86) Ubuntu 20.04/22.04 交叉编译工具链 NFS服务器 TFTP服务器 Qt Creator / 源码 编译 挂载 下载 目标板(ARM) i.MX6ULL / STM32MP1 Bootloader (U-Boot) Linux内核 根文件系统(NFS) Qt应用 开发流程 1. PC上编写/编译代码 2. 通过TFTP下载内核 3. 通过NFS挂载文件系统 4. 目标板运行Qt应用

1.1 交叉编译工具链:为什么需要它?

你想想看,你的PC是x86架构,目标板是ARM架构。PC上gcc编译出来的程序,ARM板根本跑不了。这就是指令集不兼容的问题。

交叉编译工具链,说白了就是在PC上运行、但能生成ARM机器码的编译器。我习惯用Linaro提供的工具链,稳定、社区活跃、文档齐全。

安装步骤(以ARM Cortex-A7为例)

  1. 下载工具链:从Linaro官网下载 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
  2. 解压到指定目录:我一般放在 /opt/toolchains/ 下
  3. 配置环境变量:编辑 ~/.bashrc,添加路径
# 解压
sudo mkdir -p /opt/toolchains
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/toolchains/

# 配置环境变量(追加到 ~/.bashrc)
export PATH=$PATH:/opt/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# 使生效
source ~/.bashrc

验证是否安装成功:

arm-linux-gnueabihf-gcc --version

如果能正常显示版本号,说明工具链已经就绪。我曾经遇到过PATH写错导致找不到命令的情况,排查了半天才发现是少写了一个斜杠。嗯,细节决定成败。

交叉编译一个Hello World试试

// hello.c
#include <stdio.h>

int main() {
    printf("Hello, Embedded Linux!\n");
    return 0;
}
# 交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c

# 查看生成的文件类型
file hello
# 输出:hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3

看到 ARM 字样了吗?这说明编译出来的程序是ARM架构的,放到目标板上就能跑。

1.2 NFS网络文件系统:让目标板共享PC的文件

开发阶段,你不可能每次修改代码都重新烧写整个文件系统。太慢了,也太蠢了。NFS(Network File System)就是干这个的——目标板通过网络挂载PC上的一个目录,直接运行里面的程序。

我个人的习惯是:在PC上建一个 /home/<用户名>/nfs_rootfs 目录,里面放好根文件系统,目标板启动后直接挂载过来。这样我在PC上修改代码、交叉编译,目标板立刻就能用上新程序。

PC端(NFS服务器)配置

  1. 安装NFS服务器
  2. 创建共享目录
  3. 编辑导出配置文件
  4. 重启NFS服务
# 安装NFS服务器
sudo apt-get update
sudo apt-get install nfs-kernel-server

# 创建共享目录(假设你的根文件系统在 /home/developer/nfs_rootfs)
sudo mkdir -p /home/developer/nfs_rootfs

# 编辑 /etc/exports,添加以下内容
sudo vim /etc/exports
# 写入:/home/developer/nfs_rootfs *(rw,sync,no_root_squash,no_subtree_check)

# 重启NFS服务
sudo systemctl restart nfs-kernel-server

# 验证是否生效
showmount -e localhost
参数 含义
rw 允许读写
sync 同步写入,保证数据一致性
no_root_squash 允许root用户拥有完整权限
no_subtree_check 禁用子树检查,提升性能

注意: no_root_squash 在生产环境中不要用,有安全风险。但开发阶段为了方便,我一般都会加上。曾经有一次我忘了加这个参数,结果目标板上root用户无法写文件,排查了好久才发现是权限问题。

目标板端(NFS客户端)挂载

在U-Boot启动参数中设置,或者进入系统后手动挂载:

# 手动挂载(在目标板上执行)
mount -t nfs -o nolock 192.168.1.100:/home/developer/nfs_rootfs /mnt

# 查看是否挂载成功
df -h | grep nfs

如果看到类似 192.168.1.100:/home/developer/nfs_rootfs 的条目,说明挂载成功了。这时候你在PC上往这个目录里放文件,目标板上立刻就能看到。

1.3 TFTP服务器:快速传输内核和镜像

TFTP(Trivial File Transfer Protocol)比NFS更轻量,主要用于U-Boot阶段下载内核、设备树、内核模块等。U-Boot本身不支持NFS,但支持TFTP。

我一般这样分工:TFTP负责传内核和dtb,NFS负责挂载根文件系统。各司其职,效率最高。

安装和配置TFTP服务器

# 安装TFTP服务器
sudo apt-get install tftpd-hpa

# 编辑配置文件 /etc/default/tftpd-hpa
sudo vim /etc/default/tftpd-hpa

# 内容如下:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"          # TFTP根目录
TFTP_ADDRESS="0.0.0.0:69"          # 监听所有IP的69端口
TFTP_OPTIONS="-l -c -s"            # -c 允许上传文件

# 创建TFTP根目录并设置权限
sudo mkdir -p /tftpboot
sudo chmod 777 /tftpboot

# 重启TFTP服务
sudo systemctl restart tftpd-hpa

# 验证服务是否启动
sudo systemctl status tftpd-hpa

测试TFTP是否正常工作:

# 在PC上创建一个测试文件
echo "Hello TFTP" > /tftpboot/test.txt

# 在目标板U-Boot中下载测试
tftp 0x80800000 test.txt

# 如果能成功下载,说明TFTP配置正确

U-Boot中通过TFTP启动内核的典型流程

# 设置网络参数(在U-Boot命令行中)
setenv ipaddr 192.168.1.10        # 目标板IP
setenv serverip 192.168.1.100     # PC(TFTP服务器)IP
setenv netmask 255.255.255.0
saveenv

# 下载内核和设备树
tftp 0x80800000 zImage
tftp 0x83000000 imx6ull-14x14-evk.dtb

# 设置内核启动参数
setenv bootargs console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.100:/home/developer/nfs_rootfs,proto=tcp rw ip=192.168.1.10

# 启动内核
bootz 0x80800000 - 0x83000000

1.4 网络配置与常见问题排查

这三样东西都依赖网络。网络不通,一切白搭。我遇到过最坑的情况是:防火墙没关,TFTP和NFS的端口被挡住了,折腾了一下午才发现。

服务 默认端口 常见问题
NFS 2049 防火墙未放行、exports配置错误、权限不足
TFTP 69 (UDP) 防火墙未放行、目录权限不对、文件不存在
SSH 22 用于后续调试,建议一并配置

防火墙排查命令:

# 查看防火墙状态
sudo ufw status

# 临时关闭防火墙(开发环境建议)
sudo ufw disable

# 或者放行特定端口
sudo ufw allow 2049
sudo ufw allow 69/udp

我曾经在客户现场调试,死活连不上NFS,最后发现是客户内网有严格的防火墙策略。从那以后,我每次搭建环境第一件事就是检查防火墙。

1.5 环境验证:跑一个完整的启动流程

所有配置完成后,建议做一次完整的验证:

  1. PC端:确认NFS和TFTP服务都在运行
  2. 目标板:上电,进入U-Boot
  3. 网络测试:在U-Boot中ping PC的IP
  4. TFTP测试:下载内核镜像
  5. 启动内核:通过NFS挂载根文件系统
  6. 运行Qt程序:在目标板上执行交叉编译好的Qt应用

验证清单:

  • ✅ 交叉编译工具链能正常编译ARM程序
  • ✅ NFS共享目录在目标板上可读写
  • ✅ TFTP能正常下载文件
  • ✅ 目标板能通过NFS启动并运行Qt应用

这三样都通了,你的嵌入式Linux开发环境就算搭建完成了。后面写代码、调试、优化,都会非常顺畅。

说实话,环境搭建这一步虽然繁琐,但一旦配好了,后面开发效率会非常高。我见过太多人因为环境没配好,浪费了大量时间在"为什么跑不起来"上。磨刀不误砍柴工,这一章的内容值得你花时间彻底搞懂。