1. 嵌入式Linux开发环境搭建:交叉编译工具链、TFTP/NFS服务器、内核源码树配置

说实话,很多初学者一上来就急着写驱动代码,结果卡在最基础的环境搭建上。我见过太多人花了一周时间编译内核,最后发现是工具链版本不对。嗯,这节课我们就先把地基打牢。

本章核心目标: 搭建一套可复用的嵌入式Linux开发环境,涵盖交叉编译、网络引导和内核源码管理三大模块。

1.1 交叉编译工具链:为什么需要它?

你想想看,你的PC是x86架构,而嵌入式板子通常是ARM、RISC-V或MIPS。PC上编译出来的程序,板子根本跑不了。交叉编译,说白了就是在PC上生成目标板能执行的二进制文件。

我个人习惯用Linaro GCC,它对ARM架构支持最好。当然,你也可以用芯片厂商提供的工具链,比如NXP的、TI的。但要注意版本匹配——内核版本和GCC版本有对应关系。

我的选择: 对于ARM Cortex-A系列,我推荐 gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf。这个版本稳定,社区支持好。

安装步骤其实很简单,解压后配置环境变量就行:

# 下载并解压
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
避坑指南: 我曾经因为忘记设置 CROSS_COMPILE 变量,编译出来的内核直接在板子上跑不起来。检查了三天才发现是工具链前缀没加。记住:ARCH 和 CROSS_COMPILE 必须配对。

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
小技巧: 把编译好的 zImage 和 .dtb 文件直接丢到 /srv/tftp 目录下。U-Boot 里设置 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
注意: no_root_squash 这个选项很重要。我曾经没加这个,板子上 root 用户对文件系统没有写权限,驱动模块 insmod 都报错。加上后问题解决。

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到开发板的完整数据流。我画这张图的时候,特意把关键路径标红了。

嵌入式Linux开发环境搭建流程 嵌入式Linux开发环境搭建流程 PC开发机 (x86) 目标板 (ARM) 交叉编译工具链 内核源码树 zImage + .dtb + 模块 TFTP 服务器 NFS 根文件系统 U-Boot 启动 ↓ 加载内核 (TFTP) ↓ 挂载根文件系统 (NFS) 关键路径说明: 1. PC上通过交叉编译工具链编译内核源码树,生成 zImage、设备树和模块 2. zImage 和设备树通过 TFTP 传输到目标板内存 3. 根文件系统通过 NFS 挂载,实现无盘启动和快速迭代

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!
经验之谈: 如果 insmod 报错 "Invalid module format",八成是内核版本或配置不匹配。检查一下 modinfo hello.ko 显示的 vermagic 是否和板子上 uname -r 一致。

1.6 常见问题与排查

问题 可能原因 解决方案
TFTP超时 防火墙未关闭 sudo ufw disable 或开放69端口
NFS挂载失败 权限或路径错误 检查 /etc/exports 和目录权限
内核编译报错 工具链版本不匹配 使用Linaro推荐版本,不要用太新的GCC
模块加载失败 vermagic不一致 确保模块和内核使用同一份源码编译

好了,环境搭建就到这里。这些步骤我反复验证过,跟着做应该不会出大问题。如果卡住了,先检查环境变量,再检查网络,最后看内核配置——90%的问题都出在这三个地方。