2. 开发环境搭建:交叉编译工具链配置、内核源码树准备、QEMU模拟环境搭建、NFS根文件系统挂载
说实话,做嵌入式Linux驱动开发,环境搭建这一步最容易被忽视。很多人急着写代码,结果编译都过不了,白白浪费时间。我刚开始带团队时,就遇到过新人花了两天没配好环境,最后发现是路径写错了。
这一章,咱们就把这些基础工作一次性搞定。你跟着我的步骤走,保证不出错。
2.1 交叉编译工具链配置
交叉编译,说白了就是在你的PC上编译出能在ARM板子上跑的程序。为什么不能直接在板子上编译?因为板子性能太弱,编译个内核得等半天。
我个人习惯用Linaro提供的工具链,稳定且社区支持好。以ARM Cortex-A9为例,我们选择gcc-arm-linux-gnueabihf。
核心要点:工具链版本必须与内核版本匹配。比如Linux 4.19内核,建议用gcc 7.x或8.x系列。
安装步骤
# 下载工具链
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/
# 配置环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm
小技巧:把环境变量写到~/.bashrc里,这样每次打开终端就不用重新设置了。我习惯在文件末尾加一行source /opt/env_setup.sh,统一管理所有环境变量。
验证工具链
arm-linux-gnueabihf-gcc --version
# 输出类似:arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0
嗯,这里要注意:如果你用的是Ubuntu 20.04以上版本,可能会遇到libncurses.so.5缺失的问题。我曾经被这个坑过,折腾了半天才发现是32位库没装。
sudo apt install libncurses5:i386 libstdc++6:i386
2.2 内核源码树准备
驱动开发离不开内核源码。你想想看,驱动里用到的数据结构、函数接口,全在内核头文件里定义着。没有源码树,你连struct net_device长啥样都不知道。
我建议用LTS版本的内核,比如4.19或5.10。这些版本维护周期长,社区资料也多。
下载与配置
# 从kernel.org下载
wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.237.tar.xz
tar -xvf linux-4.19.237.tar.xz
cd linux-4.19.237
# 配置内核(使用默认的ARM配置)
make vexpress_defconfig
make menuconfig
注意:千万别跳过make menuconfig这一步。默认配置可能没开启你需要的网络驱动选项。比如你要写虚拟网卡驱动,得确保CONFIG_TUN或CONFIG_VETH是开启的。
配置完成后,编译内核和设备树:
make -j4 zImage
make -j4 dtbs
编译好的内核镜像在arch/arm/boot/zImage,设备树在arch/arm/boot/dts/vexpress-v2p-ca9.dtb。
2.3 QEMU模拟环境搭建
没有开发板怎么办?用QEMU模拟。说实话,我平时调试驱动,80%的工作都在QEMU上完成。只有最后验证硬件时才上真板子。
为什么?因为QEMU可以快照、可以单步调试、可以随时重启。这些在真板子上做起来太费劲了。
安装QEMU
sudo apt install qemu-system-arm qemu-efi
启动模拟器
qemu-system-arm \
-M vexpress-a9 \
-kernel linux-4.19.237/arch/arm/boot/zImage \
-dtb linux-4.19.237/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-append "console=ttyAMA0,115200 root=/dev/mmcblk0 rw" \
-sd rootfs.ext3 \
-nographic
参数说明:
-M vexpress-a9:模拟ARM Versatile Express开发板-kernel:指定内核镜像-dtb:指定设备树文件-append:内核启动参数-sd:SD卡镜像文件(根文件系统)
我第一次用QEMU时,忘了加-nographic参数,结果图形窗口弹出来,终端里啥也看不到。后来才知道,服务器环境一般都用串口控制台。
2.4 NFS根文件系统挂载
用SD卡镜像做根文件系统有个问题:每次修改文件都得重新制作镜像,太慢了。更好的做法是用NFS挂载,让QEMU通过网络访问你PC上的目录。
这样做的好处是:你在PC上修改代码,板子上立刻就能看到变化。调试驱动时特别方便。
配置NFS服务器
# 安装NFS服务器
sudo apt install nfs-kernel-server
# 创建共享目录
mkdir -p /srv/nfs/rootfs
# 解压根文件系统(用Buildroot或Debian的armhf根文件系统)
sudo tar -xvf armhf-rootfs.tar.gz -C /srv/nfs/rootfs
# 配置exports文件
echo "/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
避坑指南:我曾经忘了加no_root_squash选项,结果板子上用root用户写文件时,权限全变成了nobody。折腾了两小时才发现是NFS权限映射的问题。
修改内核启动参数
qemu-system-arm \
-M vexpress-a9 \
-kernel linux-4.19.237/arch/arm/boot/zImage \
-dtb linux-4.19.237/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-append "console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs rw ip=192.168.1.101::192.168.1.1:255.255.255.0::eth0:off" \
-netdev user,id=net0,net=192.168.1.0/24,dhcpstart=192.168.1.100 \
-device virtio-net-pci,netdev=net0 \
-nographic
这里nfsroot参数指定了NFS服务器的IP和共享路径。ip参数配置了板子的网络地址。你想想看,没有网络,NFS怎么挂载?所以网络配置必须正确。
验证挂载
启动QEMU后,如果看到类似下面的日志,说明NFS挂载成功了:
IP-Config: Complete:
device=eth0, hwaddr=52:54:00:12:34:56, ipaddr=192.168.1.101, mask=255.255.255.0, gw=192.168.1.1
VFS: Mounted root (nfs filesystem) on device 0:12.
2.5 本章知识体系
下面这张图展示了整个开发环境的架构关系:
从这张图可以看得很清楚:主机上的交叉编译工具链编译内核和驱动,生成ARM架构的二进制文件。QEMU模拟ARM硬件环境,运行这些二进制文件。而NFS则让主机和QEMU共享文件系统,实现快速迭代。
总结一下:这套环境搭建好之后,你写驱动的工作流就变成了:
- 在主机上写代码
- 用交叉编译工具链编译
- 把编译好的模块放到NFS共享目录
- 在QEMU里insmod加载测试
整个过程不用重启、不用烧写,效率非常高。
好了,环境搭建就到这里。下一章咱们开始写第一个网络驱动——一个最简单的虚拟网卡。到时候你就知道,前面这些准备工作有多重要了。
公众号:蓝海资料掘金营,微信deep3321