第二章 嵌入式Linux开发环境搭建:交叉编译工具链、SSH/NFS网络配置、目标板与宿主机通信
做嵌入式AI部署,第一步不是写代码,而是把开发环境搭好。我见过太多新手卡在环境上,代码写好了却跑不起来,最后发现是工具链版本不对。说白了,嵌入式开发不像PC开发,你不能直接在目标板上写代码、编译——ARM芯片跑个Linux桌面都费劲,更别说跑GCC了。
所以我们需要一套「宿主机 + 目标板」的协作模式。宿主机(你的PC或服务器)负责编译,目标板(开发板)负责运行。中间靠交叉编译工具链、SSH和NFS来沟通。这一章,我就带你把这些基础打牢。
本章核心目标:在宿主机上搭建交叉编译环境,配置SSH远程登录和NFS文件共享,实现宿主机与目标板之间的高效通信。
2.1 交叉编译工具链:为什么需要它?
你想想看,你的PC是x86架构,目标板是ARM架构。x86的机器码ARM CPU根本不认识。所以我们需要一套在x86上运行、但能生成ARM机器码的编译器——这就是交叉编译工具链。
我个人习惯用Linaro提供的预编译工具链,稳定且社区支持好。当然,你也可以用芯片厂商提供的SDK,比如瑞芯微的RKNN Toolkit里就带了配套的交叉编译器。
2.1.1 工具链安装步骤
- 下载工具链:从Linaro官网或芯片厂商获取aarch64-linux-gnu-gcc包。
- 解压到指定目录:我一般放在
/opt/toolchains/下,方便管理。 - 配置环境变量:将工具链的bin目录加入
PATH。 - 验证安装:运行
aarch64-linux-gnu-gcc --version。
# 下载Linaro GCC 11.3(以aarch64为例)
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
# 解压
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt/toolchains/
# 配置环境变量(写入 ~/.bashrc)
export PATH=/opt/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64
# 使生效
source ~/.bashrc
# 验证
aarch64-linux-gnu-gcc --version
我的经验:工具链版本一定要和内核版本匹配。我曾经在一个项目里用了太新的GCC编译旧内核,结果编译出来的驱动模块加载时报错「Invalid module format」。后来换成Linaro 7.5就没事了。建议先查目标板内核的GCC版本要求。
2.1.2 交叉编译一个Hello World
装好工具链后,我们来验证一下。写个简单的C程序,用交叉编译器编译,然后放到目标板上运行。
// hello.c
#include <stdio.h>
int main() {
printf("Hello from ARM64!\n");
return 0;
}
# 交叉编译
aarch64-linux-gnu-gcc -o hello_arm hello.c
# 查看文件信息
file hello_arm
# 输出: hello_arm: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1
# 注意:不能在x86 PC上直接运行!
./hello_arm
# 输出: bash: ./hello_arm: cannot execute binary file: Exec format error
看到「Exec format error」就对了——这说明它确实是ARM的二进制文件。接下来我们需要把它传到目标板上。
2.2 SSH网络配置:远程登录目标板
SSH是嵌入式开发中最常用的远程登录方式。我习惯在目标板上用dropbear作为SSH服务端,它比OpenSSH轻量得多,适合资源受限的嵌入式设备。
2.2.1 目标板端配置
如果你的开发板系统是Buildroot或Yocto构建的,通常已经集成了dropbear。如果没有,手动安装也很简单:
# 在目标板上(假设有网络)
apt-get update
apt-get install dropbear
# 启动dropbear
/etc/init.d/dropbear start
# 设置开机自启
update-rc.d dropbear defaults
2.2.2 宿主机端连接
确保宿主机和目标板在同一个局域网内。用一根网线直连或者通过路由器连接都可以。
# 查看目标板IP(在目标板上执行)
ifconfig eth0
# 假设目标板IP是 192.168.1.100
# 在宿主机上SSH登录
ssh root@192.168.1.100
# 首次登录会提示确认主机密钥,输入yes
# 然后输入密码(默认root密码通常是空或123456)
注意:嵌入式设备的SSH默认密码一定要改!我见过不少开发板因为默认密码被扫描到,结果被植入挖矿程序。建议登录后立即执行passwd修改密码。
2.2.3 配置静态IP(推荐)
动态IP每次重启都可能变化,很麻烦。我建议给目标板配静态IP,这样SSH连接和后续的NFS挂载都更稳定。
# 编辑 /etc/network/interfaces
auto eth0
iface eth0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1
dns-nameservers 8.8.8.8
# 重启网络服务
/etc/init.d/networking restart
2.3 NFS网络文件系统:让目标板共享宿主机目录
SSH可以传文件,但每次修改代码都要scp一遍,效率太低。NFS(网络文件系统)可以让你在宿主机上修改文件,目标板实时看到变化——这对调试模型部署代码来说太方便了。
2.3.1 宿主机端配置NFS Server
# 安装NFS服务器
sudo apt-get install nfs-kernel-server
# 创建共享目录
sudo mkdir -p /srv/nfs/target_root
# 设置权限(根据你的用户ID调整)
sudo chown -R $USER:$USER /srv/nfs/target_root
# 编辑 /etc/exports,添加共享配置
echo "/srv/nfs/target_root 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
# 验证共享是否生效
showmount -e localhost
# 输出: Export list for localhost:
# /srv/nfs/target_root 192.168.1.0/24
避坑指南:我曾经在/etc/exports里忘了加no_root_squash,结果目标板上的root用户无法写入共享目录。如果你在目标板上以root身份操作,一定要加上这个选项。
2.3.2 目标板端挂载NFS
# 在目标板上安装NFS客户端
apt-get install nfs-common
# 创建挂载点
mkdir -p /mnt/nfs_share
# 挂载宿主机共享目录(宿主机IP假设为192.168.1.10)
mount -t nfs 192.168.1.10:/srv/nfs/target_root /mnt/nfs_share
# 验证挂载
df -h | grep nfs
ls /mnt/nfs_share
挂载成功后,你在宿主机/srv/nfs/target_root下放任何文件,目标板上都能直接看到。我把编译好的推理程序和模型文件都放在这个目录下,调试时直接在目标板上运行,改代码后重新编译,目标板立刻就能用新版本。
2.3.3 设置开机自动挂载
每次重启都要手动mount太烦了。配置一下自动挂载:
# 编辑 /etc/fstab,添加一行
echo "192.168.1.10:/srv/nfs/target_root /mnt/nfs_share nfs defaults,noatime 0 0" >> /etc/fstab
# 测试自动挂载
mount -a
2.4 开发环境验证清单
环境搭好后,我建议你按这个清单逐项验证,确保每个环节都通:
| 验证项 | 命令/操作 | 预期结果 |
|---|---|---|
| 交叉编译 | aarch64-linux-gnu-gcc -o test test.c |
生成ARM64可执行文件 |
| SSH登录 | ssh root@192.168.1.100 |
成功登录目标板shell |
| 文件传输 | scp test root@192.168.1.100:/tmp/ |
文件成功传输 |
| NFS挂载 | mount | grep nfs |
显示NFS挂载信息 |
| 远程运行 | 在目标板上执行/mnt/nfs_share/test |
程序正常输出 |
我的工作流:代码在宿主机上用VS Code编写,保存后自动触发交叉编译脚本,编译产物直接输出到NFS共享目录。目标板上开一个SSH终端,随时运行最新版本。整个流程从改代码到看到结果,不超过10秒。
2.5 常见问题与排查
环境搭建过程中难免遇到问题。这里列几个我踩过的坑:
- SSH连接超时:检查防火墙是否开放22端口,或者目标板上的dropbear是否在监听。
netstat -tlnp | grep 22可以查看。 - NFS挂载卡死:通常是网络不通或
/etc/exports配置有误。先试试ping宿主机IP,然后在宿主机上exportfs -v查看共享是否生效。 - 交叉编译出来的程序在目标板上报错:最常见的原因是动态库不匹配。用
ldd查看依赖,确保目标板上有对应的库文件。或者直接静态编译:aarch64-linux-gnu-gcc -static -o test test.c。 - 权限问题:目标板上的用户无法读写NFS目录。检查
/etc/exports中的no_root_squash选项,以及共享目录的权限设置。
重要提醒:不要在生产环境或公网上暴露你的NFS共享。NFS协议本身没有加密,所有数据都是明文传输。开发环境用用没问题,但千万别图省事直接用在产品上。