1. 嵌入式Linux驱动开发环境搭建
做驱动开发,第一件事不是写代码,而是搭环境。我见过太多人上来就撸代码,结果编译报错半天发现是工具链没配好——白白浪费时间。这一章,咱们把交叉编译工具链、内核源码树、NFS/TFTP网络启动这三块地基打牢。
1.1 交叉编译工具链配置
说白了,交叉编译就是在PC上编译出能在ARM板子上跑的程序。你想想看,你的开发机是x86架构,目标板是ARM架构,不用交叉编译,难道把PC扛到板子旁边去编译吗?
1.1.1 工具链的选择
我个人习惯用Linaro提供的GCC工具链,稳定且社区活跃。当然你也可以用芯片厂商提供的,比如NXP的、TI的。这里我列个对比:
| 工具链来源 | 适用平台 | 优点 | 缺点 |
|---|---|---|---|
| Linaro GCC | ARM Cortex-A系列 | 通用性强,更新快 | 对特定芯片优化不够 |
| 芯片厂商SDK | 特定SoC | 硬件优化好,BSP配套全 | 版本老旧,绑定厂商 |
| Buildroot/Yocto | 自定义系统 | 全套工具链+根文件系统 | 构建时间长,学习曲线陡 |
1.1.2 安装与配置步骤
以Linaro 7.5版本为例,我通常这样操作:
# 下载工具链
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 里,每次打开终端自动生效。我习惯再加一行 alias armcc='arm-linux-gnueabihf-gcc',少敲几个字符。
1.1.3 验证工具链
配好了得验证一下,别等到编译内核时报错才回头查:
arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc -v 2>&1 | grep "Target:"
输出应该看到 arm-linux-gnueabihf 字样。我曾经遇到一次,明明配了路径,但 --version 显示的却是x86的gcc——原来是系统自带的gcc优先级更高,PATH顺序搞反了。
/opt 或 /usr/local 都行,但记得给目录加写权限。我吃过亏,sudo装完结果普通用户编译不了。
1.2 内核源码树构建
内核源码树,说白了就是Linux内核的完整代码目录结构。驱动编译需要它提供的头文件和Makefile规则。没有内核源码树,你连个hello world驱动都编译不出来。
1.2.1 获取内核源码
我建议直接从kernel.org下载主线内核,或者用芯片厂商提供的BSP内核。这里以Linux 5.10 LTS为例:
# 下载主线内核
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
# 解压
tar -xvf linux-5.10.100.tar.xz
cd linux-5.10.100
1.2.2 配置内核
内核配置有三种方式,我列个表给你看:
| 配置方式 | 命令 | 适用场景 |
|---|---|---|
| 默认配置 | make defconfig |
快速开始,通用平台 |
| 芯片厂商配置 | make <vendor>_defconfig |
特定开发板,如 make imx_v7_defconfig |
| 菜单配置 | make menuconfig |
手动裁剪内核模块 |
我个人习惯先用厂商的defconfig,再用menuconfig微调。嗯,这里要注意:menuconfig依赖ncurses库,没装的话先 sudo apt install libncurses-dev。
1.2.3 编译内核与模块
# 设置环境变量(如果还没设)
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 编译内核
make -j4 zImage
# 编译设备树
make -j4 dtbs
# 编译内核模块
make -j4 modules
-j4 表示用4个线程并行编译。我机器是8核的,经常用 -j$(nproc) 自动检测核心数。第一次编译内核大概要10-15分钟,后面增量编译就快多了。
arch/arm/boot/zImage— 内核镜像arch/arm/boot/dts/<你的板子>.dtb— 设备树二进制文件drivers/.../*.ko— 内核模块(.ko文件)
1.3 NFS/TFTP网络启动环境搭建
做驱动开发,最烦的就是每次修改都要烧写固件。用网络启动,板子从服务器加载内核和根文件系统,改完代码直接重启就行——省掉烧写的功夫,效率翻倍。
1.3.1 TFTP服务器配置
TFTP用来传输内核镜像和设备树文件。配置很简单:
# 安装TFTP服务器
sudo apt install tftpd-hpa
# 配置TFTP目录
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp
# 复制内核和设备树到TFTP目录
cp arch/arm/boot/zImage /srv/tftp/
cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /srv/tftp/
配置文件在 /etc/default/tftpd-hpa,我一般改成这样:
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure --create"
改完重启服务:sudo systemctl restart tftpd-hpa。
sudo ufw allow 69/udp。
1.3.2 NFS服务器配置
NFS用来共享根文件系统。板子启动后,直接挂载PC上的目录作为根目录:
# 安装NFS服务器
sudo apt install nfs-kernel-server
# 创建根文件系统目录(假设你已经用busybox或buildroot做好了)
sudo mkdir -p /srv/nfs/rootfs
# 配置导出
sudo vim /etc/exports
# 添加一行:
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
no_root_squash 这个选项很重要——它允许板子上的root用户拥有PC上root的权限。没有这个,你在板子上创建文件会报权限错误。
1.3.3 U-Boot启动参数设置
板子上电后,在U-Boot命令行设置启动参数。以i.MX6ULL为例:
# 设置IP地址(根据你的网络环境调整)
setenv serverip 192.168.1.100 # PC的IP
setenv ipaddr 192.168.1.101 # 板子的IP
# 设置TFTP加载内核和设备树
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-evk.dtb; bootz 80800000 - 83000000'
# 设置NFS根文件系统
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs,vers=3 rw ip=192.168.1.101'
# 保存并启动
saveenv
boot
我第一次配这个的时候,忘了设 vers=3,结果NFS挂载失败。U-Boot默认用NFSv2,但现代服务器都跑v3/v4,得显式指定版本。
1.4 整体流程梳理
说了这么多,我画张图帮你理清整个环境搭建的脉络:
1.5 避坑指南
最后,我把这些年踩过的坑总结一下:
- 工具链版本不匹配: 我曾经用7.5的工具链编译5.4的内核,结果某些内联汇编语法不兼容。后来我养成了习惯——查内核源码的
Documentation/process/changes.rst,看看推荐哪个gcc版本。 - 内核配置遗漏: 网络启动需要开启
CONFIG_ROOT_NFS和CONFIG_IP_PNP。我第一次忘了配,板子启动到挂载根文件系统时直接卡死。 - TFTP文件权限: TFTP目录下的文件必须是644权限,且所有者是tftp用户。我有一回
chmod 777反而导致传输失败——因为TFTP出于安全考虑,不允许写全局可写的文件。 - NFS版本协商: U-Boot的NFS客户端比较老,跟服务器协商版本时可能失败。我建议在
/etc/exports里加上vers=3,同时在U-Boot的bootargs里也指定nfsvers=3。
ping 一下PC的IP。ping不通就查网线、IP配置;ping通了再查TFTP和NFS。分层排查,别一头扎进内核代码里。
环境搭好了,后面写驱动、配设备树才有得玩。嗯,这套流程我每换一次开发板都要走一遍,熟练了也就十分钟的事。你照着做一遍,后面会省很多事。