2、开发环境搭建:交叉编译工具链安装、内核源码树配置、最小系统制作与NFS挂载
说实话,做字符设备驱动开发,环境搭建这一步要是没弄好,后面全是坑。我见过太多新手,代码写得挺漂亮,结果编译都过不了,或者烧进去系统直接崩了。今天咱们就把这四件事儿捋清楚:交叉编译工具链、内核源码树、最小系统、NFS挂载。
2.1 交叉编译工具链安装
先说说交叉编译。你想想看,咱们的PC是x86架构,但嵌入式设备通常是ARM、RISC-V或者MIPS。在PC上编译出能在ARM上跑的程序,这就是交叉编译。
我个人习惯用Linaro提供的工具链,稳定且社区活跃。以ARM Cortex-A7为例,安装步骤其实很简单:
# 下载ARM交叉编译工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/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=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
这里有个坑,我曾经遇到过:解压后直接编译,报错说找不到库。后来发现是64位系统上缺少32位兼容库。解决办法很简单:
sudo apt install lib32z1 lib32ncurses5 lib32stdc++6
验证工具链是否安装成功,跑一下这个命令:
arm-linux-gnueabihf-gcc --version
如果能看到版本信息,说明工具链已经就绪。嗯,这一步其实很简单,但很多人会忽略环境变量的持久化配置。我建议你把它写到 ~/.bashrc 里:
echo 'export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin' >> ~/.bashrc
source ~/.bashrc
2.2 内核源码树配置
驱动开发离不开内核源码树。说白了,你写的驱动模块需要依赖内核的头文件和编译规则。我刚开始做驱动时,以为随便下一个内核源码就能用,结果编译出来的模块一加载就报版本不匹配。
为什么会这样?因为内核模块必须与运行中的内核版本完全一致。所以正确的做法是:
- 获取内核源码:从你的开发板厂商获取,或者从kernel.org下载对应版本
- 配置内核:使用开发板提供的默认配置
- 编译内核:生成
vmlinux和模块
# 以树莓派3B+为例
git clone --depth=1 https://github.com/raspberrypi/linux
cd linux
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage modules dtbs
这里有个小技巧:第一次编译内核时,别急着加 -j 参数,先单线程跑一遍,确认没有语法错误。我吃过这个亏,多线程编译时错误信息被冲掉了,排查了半天。
编译完成后,你会得到几个关键文件:
| 文件 | 说明 |
|---|---|
| arch/arm/boot/zImage | 压缩后的内核镜像 |
| arch/arm/boot/dts/*.dtb | 设备树二进制文件 |
| vmlinux | 未压缩的内核,用于调试 |
| modules | 内核模块,需要安装到根文件系统 |
/home/developer/kernel/ 下,然后创建一个软链接:
ln -s /home/developer/kernel /home/developer/linux-5.10
这样在写驱动Makefile时,路径更清晰。
2.3 最小系统制作
最小系统,说白了就是能让内核跑起来的最简根文件系统。你想想看,一个嵌入式设备不需要桌面环境,只需要基本的shell、库文件和驱动模块。
我推荐用Buildroot来制作,它比Yocto轻量得多,适合快速验证。当然,你也可以用Busybox手动搭建,但步骤会繁琐一些。
用Buildroot的步骤:
# 下载Buildroot
git clone https://github.com/buildroot/buildroot.git
cd buildroot
# 配置(以树莓派3为例)
make raspberrypi3_defconfig
# 自定义配置
make menuconfig
在menuconfig里,你需要关注几个关键选项:
- Target options:选择ARM架构和具体的CPU型号
- Toolchain:选择外部工具链(就是我们刚才安装的那个)
- System configuration:设置主机名、root密码等
- Filesystem images:选择ext4格式,方便后续挂载
配置完成后,直接编译:
make -j4
编译完成后,在 output/images/ 下会生成 rootfs.ext4 文件。这就是我们的最小系统。
- /dev/console 和 /dev/null 设备节点
- /etc/inittab 初始化脚本
- /lib 下的动态链接库
- /sbin/init 或 busybox
2.4 NFS挂载
开发阶段,每次修改驱动都要重新烧写根文件系统,那效率太低了。NFS挂载可以让你在PC上共享一个目录,开发板通过网络挂载它作为根文件系统。
我当年第一次用NFS时,折腾了一整天,最后发现是防火墙没关。嗯,这里把步骤说清楚:
PC端配置(Ubuntu):
# 安装NFS服务器
sudo apt install nfs-kernel-server
# 创建共享目录
sudo mkdir -p /srv/nfs/rootfs
# 把最小系统解压到共享目录
sudo tar -xvf rootfs.ext4 -C /srv/nfs/rootfs/
# 配置exports文件
sudo vim /etc/exports
# 添加一行:
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
开发板端配置(U-Boot):
# 设置内核启动参数
setenv bootargs 'console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs rw ip=192.168.1.101:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'
saveenv
boot
这里有几个参数需要解释一下:
nfsroot:PC的IP和共享目录路径ip:开发板的IP、服务器IP、网关、子网掩码no_root_squash:允许root用户访问,否则权限会出问题
2.5 本章知识体系
为了让你更直观地理解整个环境搭建的流程,我画了一张图:
这张图展示了四个模块之间的依赖关系。说白了,交叉编译工具链是基础,它用来编译内核源码树和最小系统。而NFS挂载则是开发阶段的加速器,让你不用反复烧写SD卡。
好了,环境搭建这部分就讲到这里。记住:工具链、内核、根文件系统、NFS,这四个东西配齐了,你就能舒舒服服地开始写驱动了。下一章咱们开始写第一个字符设备驱动——"hello world"。