2. 嵌入式Linux开发环境搭建:把“手术台”支起来
做嵌入式Linux开发,说白了就是“交叉编译”。你想想看,咱们的llama.cpp要在ARM板子上跑,但代码却是在x86的PC上写的。这就好比你要给一个病人做手术,但手术台太小,你只能在另一张桌子上把器械准备好,再拿过去用。
所以,环境搭建的第一步——你得把这张“准备桌”支起来。今天我就带你过一遍我个人的标准流程。
2.1 安装交叉编译工具链
交叉编译工具链,就是能在PC上生成ARM可执行文件的“编译器全家桶”。我常用的有两个:
| 工具链名称 | 目标架构 | 适用场景 |
|---|---|---|
| gcc-arm-linux-gnueabihf | 32位ARM (armv7-a) | 树莓派2/3,全志H3等老款芯片 |
| gcc-aarch64-linux-gnu | 64位ARM (armv8-a) | 树莓派3B+/4/5,RK3568/RK3588等 |
个人经验: 如果你不确定选哪个,可以去板子的官方文档看“uname -m”的输出。如果是“armv7l”,选32位;如果是“aarch64”,选64位。我在一个项目里吃过亏,拿32位工具链去编译64位的库,折腾了一整天。
2.1.1 从包管理器安装(推荐新手)
Debian/Ubuntu系统下,直接写:
# 安装 32 位工具链
sudo apt-get install gcc-arm-linux-gnueabihf
# 安装 64 位工具链
sudo apt-get install gcc-aarch64-linux-gnu
装完验证一下:
arm-linux-gnueabihf-gcc --version
aarch64-linux-gnu-gcc --version
输出能看到版本号,就说明装好了。简单吧?
2.1.2 手动安装Linaro工具链(我习惯的方式)
我为什么偏爱手动安装?因为版本更可控。有些生产环境必须锁定编译器版本,用apt-get往往会升级到最新版,反而出问题。
# 下载Linaro提供的预编译工具链
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 解压到指定目录
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/
# 配置环境变量
echo 'export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin' >> ~/.bashrc
source ~/.bashrc
2.2 配置TFTP与NFS
工具链装好只是第一步。你编译出来的内核和程序,怎么快速传给板子?每次插拔SD卡太原始了。这时候就要请出两位老搭档:TFTP和NFS。
- TFTP:简单文件传输,一般用来下载内核和设备树,启动Uboot时用。
- NFS:网络文件系统,把PC上的目录挂载到板子上,开发阶段直接在上面运行程序。
2.2.1 TFTP服务器配置
我用的是tftpd-hpa,轻量好用。
# 安装
sudo apt-get install tftpd-hpa
# 配置,指定TFTP根目录
sudo vi /etc/default/tftpd-hpa
# 内容如下:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure --create"
# 创建目录并设置权限
sudo mkdir -p /srv/tftp
sudo chown -R tftp:tftp /srv/tftp
# 重启服务
sudo systemctl restart tftpd-hpa
sudo systemctl enable tftpd-hpa
注意: 我曾经因为防火墙没关,折腾了半小时连不上板子。Ubuntu默认防火墙不开放UDP 69端口,你最好检查一下:sudo ufw status。如果开启,记得加规则:sudo ufw allow 69/udp。
2.2.2 NFS服务器配置
NFS才是开发阶段的大杀器。你想想看,写代码、编译、然后直接通过网络跑程序,根本不用搬文件。
# 安装NFS服务器
sudo apt-get install nfs-kernel-server
# 创建共享目录
sudo mkdir -p /srv/nfs/rootfs
sudo chown -R nobody:nogroup /srv/nfs/rootfs
# 配置导出
sudo vi /etc/exports
# 添加如下行(具体IP或网段按你网络来)
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
# 生效配置
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
关键参数说明:
rw:可读写sync:同步写入,保证数据一致性no_root_squash:允许板子上的root用户保持root权限。这个很重要,不然后面llama.cpp写日志都报权限不足。
2.3 使用QEMU模拟运行
有人会问:“我没有开发板怎么办?” 嗯,这个问题我遇到过,那会儿项目刚立项,板子还没到货。我用QEMU撑了三周,进度一点没落下。
QEMU可以模拟ARM处理器,让你在PC上“虚拟”一台开发板。我们用qemu-system-arm。
# 安装QEMU及相关支持
sudo apt-get install qemu-system-arm qemu-user-static
# 验证
qemu-system-arm --version
2.3.1 下载并测试一个简化内核
我们先用QEMU跑一个最小系统,验证整个工具链是否通畅。
# 下载vexpress开发板的内核和设备树
wget https://bootlin.com/doc/training/embedded-linux-qemu/linux-5.10.65-arm.tar.xz
tar xJf linux-5.10.65-arm.tar.xz
# 启动QEMU模拟器
qemu-system-arm -M vexpress-a9 \
-kernel linux-5.10.65-arm/arch/arm/boot/zImage \
-dtb linux-5.10.65-arm/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-nographic \
-append "console=ttyAMA0,115200 root=/dev/mmcblk0"
如果一切正常,你会看到内核启动的日志打印。看到那行“VFS: Mounted root (ext4 filesystem)”,就说明环境跑通了。
2.3.2 交叉编译一个测试程序并跑在QEMU上
环境通了,我们来验证交叉编译器能工作。
# 编写测试程序
cat > hello.c << EOF
#include <stdio.h>
int main() {
printf("Hello from QEMU ARM!\n");
return 0;
}
EOF
# 交叉编译
arm-linux-gnueabihf-gcc -static -o hello hello.c
# 查看文件类型(确保是ARM架构)
file hello
# 输出: ELF 32-bit LSB executable, ARM, EABI5 version 1 ...
把编译好的hello文件放到共享目录,启动QEMU后执行:
# 在QEMU虚拟板子上
/hello
# 输出: Hello from QEMU ARM!
看到这行字,就说明你的整个交叉开发环境——从工具链到QEMU模拟器——全部通顺了。
核心思路: 在PC上开发,在QEMU或板子上运行。环境搭建的本质就是打通“编译-传输-运行”这条链路。链路通,万事通。
避坑提醒: 我之前在一个项目里,因为NFS的版本不对(PC是NFSv4,板子里内核只支持NFSv3),挂载时一直报“RPC: Timed out”。花了半天才发现。所以配置时最好在/etc/exports里明确版本,或者用vers=3参数挂载:mount -t nfs -o nfsvers=3,noacl 192.168.1.100:/srv/nfs/rootfs /mnt