1. 嵌入式Linux开发环境搭建:交叉编译工具链、TFTP/NFS服务器、内核源码树配置
说实话,很多初学者一上来就急着写驱动代码,结果卡在最基础的环境搭建上。我见过太多人花了一周时间编译内核,最后发现是工具链版本不对。嗯,这节课我们就先把地基打牢。
1.1 交叉编译工具链:为什么需要它?
你想想看,你的PC是x86架构,而嵌入式板子通常是ARM、RISC-V或MIPS。PC上编译出来的程序,板子根本跑不了。交叉编译,说白了就是在PC上生成目标板能执行的二进制文件。
我个人习惯用Linaro GCC,它对ARM架构支持最好。当然,你也可以用芯片厂商提供的工具链,比如NXP的、TI的。但要注意版本匹配——内核版本和GCC版本有对应关系。
安装步骤其实很简单,解压后配置环境变量就行:
# 下载并解压
wget https://releases.linaro.org/components/toolchain/binaries/.../gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz
tar -xf gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz -C /opt/
# 配置环境变量(写入 ~/.bashrc)
export PATH=/opt/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
# 验证
arm-none-linux-gnueabihf-gcc --version
1.2 TFTP/NFS服务器:让板子从网络启动
为什么需要网络启动?你想想,每次修改驱动都要烧写SD卡或Flash,多浪费时间。TFTP用来传输内核镜像和设备树,NFS用来挂载根文件系统。这样你改完代码,重启板子就能看到效果。
1.2.1 TFTP服务器配置
TFTP很简单,就是个文件传输协议。我习惯用 tftp-hpa 这个包:
# Ubuntu/Debian
sudo apt-get install tftp-hpa tftpd-hpa
# 配置文件 /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
# 测试
echo "test" | sudo tee /srv/tftp/test.txt
tftp localhost -c get test.txt
tftp 0x82000000 zImage 就能加载。
1.2.2 NFS服务器配置
NFS用来共享根文件系统。板子启动后,通过NFS挂载PC上的目录作为根目录。这样你修改驱动模块,直接拷贝到NFS目录,板子上就能用。
# 安装NFS服务器
sudo apt-get install nfs-kernel-server
# 编辑 /etc/exports,添加共享目录
/opt/rootfs *(rw,sync,no_subtree_check,no_root_squash)
# 重启服务
sudo systemctl restart nfs-kernel-server
# 验证
showmount -e localhost
1.3 内核源码树配置:从源码到可引导镜像
内核源码树是驱动开发的基础。你需要下载、配置、编译,生成 zImage 和模块。我个人习惯用 Linux 5.10 LTS,长期支持版本,稳定。
1.3.1 下载与解压
# 从内核官网下载
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xf linux-5.10.100.tar.xz
cd linux-5.10.100
1.3.2 配置内核
配置内核有三种方式:
| 方式 | 命令 | 适用场景 |
|---|---|---|
| 默认配置 | make defconfig |
快速开始,通用配置 |
| 芯片厂商配置 | make <board>_defconfig |
针对特定开发板,如 make imx_v7_defconfig |
| 手动配置 | make menuconfig |
精细调整,裁剪内核 |
我建议先用厂商的 defconfig,然后通过 menuconfig 微调。比如去掉不需要的驱动,减小内核体积。
# 以 i.MX6ULL 为例
make imx_v7_defconfig
make menuconfig # 手动调整
1.3.3 编译内核
# 设置环境变量(如果之前没设)
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
# 编译内核镜像和设备树
make -j4 zImage dtbs
# 编译内核模块
make -j4 modules
# 安装模块到NFS根文件系统
make modules_install INSTALL_MOD_PATH=/opt/rootfs
arch/arm/boot/zImage— 内核镜像arch/arm/boot/dts/<board>.dtb— 设备树vmlinux— 未压缩的内核,用于调试System.map— 符号表,调试时有用
1.4 整体流程:一张图看懂
下面这张图展示了从PC到开发板的完整数据流。我画这张图的时候,特意把关键路径标红了。
1.5 验证环境:写个Hello驱动试试
环境搭好了,我们来验证一下。写一个最简单的内核模块,编译后加载到板子上。
// hello.c
#include <linux/module.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, Embedded Linux!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, Embedded Linux!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
# Makefile
obj-m += hello.o
all:
make -C /path/to/linux-5.10.100 M=$(PWD) modules
clean:
make -C /path/to/linux-5.10.100 M=$(PWD) clean
编译后得到 hello.ko,拷贝到NFS根文件系统,在板子上执行:
# insmod hello.ko
# dmesg | tail
[ 12.345678] Hello, Embedded Linux!
# rmmod hello
# dmesg | tail
[ 15.123456] Goodbye, Embedded Linux!
modinfo hello.ko 显示的 vermagic 是否和板子上 uname -r 一致。
1.6 常见问题与排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| TFTP超时 | 防火墙未关闭 | sudo ufw disable 或开放69端口 |
| NFS挂载失败 | 权限或路径错误 | 检查 /etc/exports 和目录权限 |
| 内核编译报错 | 工具链版本不匹配 | 使用Linaro推荐版本,不要用太新的GCC |
| 模块加载失败 | vermagic不一致 | 确保模块和内核使用同一份源码编译 |
好了,环境搭建就到这里。这些步骤我反复验证过,跟着做应该不会出大问题。如果卡住了,先检查环境变量,再检查网络,最后看内核配置——90%的问题都出在这三个地方。