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

说实话,嵌入式Linux开发的第一步,也是最容易让人栽跟头的一步,就是搭环境。很多新手一上来就急着写驱动,结果编译都过不了,或者烧进去跑不起来,最后发现是工具链版本不对、服务器没配好。我当年刚入行时也吃过这个亏——花了一周时间调一个网卡驱动,最后发现是TFTP传输时文件损坏了。嗯,咱们先把地基打牢。

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

你想想看,你的PC是x86架构,而嵌入式板子通常是ARM、RISC-V或者MIPS。PC上编译出来的程序,板子根本跑不了。交叉编译工具链,说白了就是一套能在PC上生成目标板可执行文件的编译器、链接器和库。

核心概念:交叉编译 = 在宿主机(x86)上编译,生成目标机(ARM)的二进制代码。

1.1.1 工具链的组成

一套完整的交叉编译工具链包含以下组件:

组件 典型名称 作用
C编译器 arm-linux-gnueabihf-gcc 编译C源码为目标机机器码
C++编译器 arm-linux-gnueabihf-g++ 编译C++源码
链接器 arm-linux-gnueabihf-ld 链接目标文件生成可执行文件
汇编器 arm-linux-gnueabihf-as 汇编源码转目标文件
调试器 arm-linux-gnueabihf-gdb 远程调试目标板程序
库文件 libc.so.6, ld-linux-armhf.so.3 运行时依赖的C库和动态链接器

我的习惯:工具链的命名规则其实很有信息量。比如 arm-linux-gnueabihf-gcc,拆开看:arm 是架构,linux 是操作系统,gnueabihf 表示使用GNU的EABI且支持硬件浮点。选错浮点类型,程序跑起来会Segmentation Fault,我踩过这个坑。

1.1.2 获取与安装工具链

获取工具链有几种方式,我按推荐程度排序:

  1. 芯片厂商官方提供(最推荐)—— 比如NXP的Linaro工具链、TI的Processor SDK。兼容性最好,我项目里一直用这个。
  2. Buildroot/Yocto生成—— 适合定制化需求,但编译一次要几个小时。
  3. Linaro预编译版本—— 社区维护,更新快,适合通用ARM平台。
  4. 自己从源码编译crosstool-NG—— 不推荐新手尝试,我曾经折腾了三天才搞定。

安装步骤很简单,以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

# 解压到指定目录
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/

# 添加环境变量
echo 'export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin' >> ~/.bashrc
source ~/.bashrc

# 验证安装
arm-linux-gnueabihf-gcc --version

注意:工具链版本必须与内核版本匹配。比如Linux 4.19内核推荐使用gcc 7.x,Linux 5.10推荐gcc 9.x。版本差太多,编译内核时会报一堆莫名其妙的错误。

1.2 TFTP/NFS服务器配置:让板子与PC通信

环境搭好了,怎么把编译好的内核和文件系统送到板子上?我早期做开发时,每次都要插拔SD卡,效率极低。后来用了TFTP传内核、NFS挂载根文件系统,改完代码直接重启板子就能看到效果,开发效率翻了好几倍。

1.2.1 TFTP服务器:快速传输内核和设备树

TFTP(Trivial File Transfer Protocol)是个轻量级的文件传输协议,UDP端口69。U-Boot默认支持它,用来从PC下载内核镜像和设备树文件。

配置步骤(Ubuntu 20.04为例):

# 安装TFTP服务器
sudo apt-get install tftpd-hpa

# 创建TFTP根目录
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp

# 修改配置文件 /etc/default/tftpd-hpa
sudo vim /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 systemctl enable tftpd-hpa

# 测试:在/srv/tftp下放一个文件,然后在另一台机器上获取
tftp 192.168.1.100 -c get test.txt

避坑指南:我曾经遇到过TFTP传输大文件(超过32MB)时总是超时。后来发现是防火墙没开UDP 69端口。另外,U-Boot的TFTP默认块大小是512字节,传输速度很慢。可以在U-Boot中设置 setenv tftpblocksize 1468 来加速。

1.2.2 NFS服务器:挂载根文件系统

NFS(Network File System)允许板子通过网络挂载PC上的目录作为根文件系统。这样你修改PC上的文件,板子立刻就能用,不用反复烧写。

配置步骤:

# 安装NFS服务器
sudo apt-get install nfs-kernel-server

# 创建共享目录,比如存放根文件系统
sudo mkdir -p /srv/nfs/rootfs

# 将构建好的根文件系统解压到该目录
sudo tar -xvf rootfs.tar.gz -C /srv/nfs/rootfs

# 修改配置文件 /etc/exports
sudo vim /etc/exports

# 添加一行(允许开发板访问)
/srv/nfs/rootfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

# 重启NFS服务
sudo systemctl restart nfs-kernel-server

# 验证挂载
showmount -e localhost

关键点:no_root_squash 这个选项必须加。不加的话,板子上的root用户会被映射成nobody,很多系统程序(比如udev、init)会因为没有权限而启动失败。我刚开始做项目时没加这个,板子启动到一半就卡住了,排查了整整一天。

1.3 内核源码树构建:从源码到可启动镜像

有了工具链和服务器,接下来就是编译内核。内核源码树的结构很庞大,但咱们只需要关注几个关键目录和配置流程。

1.3.1 获取内核源码

我建议从kernel.org下载长期支持(LTS)版本,比如4.19、5.10、6.1。这些版本维护时间长,社区支持好。

# 下载Linux 5.10 LTS
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.200.tar.xz

# 解压
tar -xvf linux-5.10.200.tar.xz
cd linux-5.10.200

1.3.2 配置内核

内核配置有三种方式,我一般这样用:

配置方式 命令 适用场景
默认配置 make defconfig 快速生成通用配置,然后手动微调
基于厂商配置 make <board>_defconfig 比如 make imx_v7_defconfig,适合特定开发板
菜单配置 make menuconfig 交互式图形界面,逐项选择驱动模块

实际操作流程:

# 设置交叉编译环境变量
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# 使用厂商默认配置(以i.MX6ULL为例)
make imx_v7_defconfig

# 进入菜单配置,按需调整
make menuconfig

# 编译内核镜像和设备树
make -j4 zImage
make -j4 dtbs

# 编译内核模块
make -j4 modules

# 安装模块到根文件系统目录
make modules_install INSTALL_MOD_PATH=/srv/nfs/rootfs

输出文件说明:

  • arch/arm/boot/zImage —— 压缩后的内核镜像,通过TFTP下载到板子
  • arch/arm/boot/dts/<board>.dtb —— 设备树二进制文件,描述硬件信息
  • /srv/nfs/rootfs/lib/modules/ —— 内核模块,挂载NFS后板子可直接使用

1.4 知识体系总览

下面这张图把本章的核心流程串起来了,你可以对照着理解:

嵌入式Linux开发环境搭建核心流程 宿主机(x86 PC / Ubuntu 20.04) 交叉编译工具链 arm-linux-gnueabihf-gcc 版本:gcc 7.5 / 9.3 TFTP/NFS服务器 TFTP: UDP 69 NFS: /srv/nfs/rootfs 内核源码树 Linux 5.10 LTS make zImage + dtbs 目标板(ARM Cortex-A7 / i.MX6ULL) U-Boot引导 内核启动 挂载NFS根文件系统 开发流程:PC编译 → TFTP传内核 → NFS挂载根文件系统 → 板子启动运行

1.5 验证环境是否搭建成功

所有配置完成后,我习惯用下面这个清单快速验证:

  1. 工具链验证:编译一个简单的Hello World程序,用 file 命令确认是ARM架构。
  2. TFTP验证:在U-Boot中执行 tftp 0x80800000 zImage,看能否正常下载。
  3. NFS验证:板子启动后执行 mount -t nfs 192.168.1.100:/srv/nfs/rootfs /mnt,看能否挂载成功。
  4. 内核启动验证:板子上电,串口输出看到 VFS: Mounted root (nfs filesystem) 即成功。

我的经验:第一次搭建环境时,别急着编译完整内核。先写一个最简单的驱动模块(Hello World),用交叉编译工具链编译,再通过TFTP传到板子上用 insmod 加载。这一步通了,说明工具链、TFTP、内核模块框架都没问题。后面再搞复杂的驱动就心里有底了。

嗯,环境搭建这部分就这些。虽然看起来步骤多,但每一步都有它的道理。你把这些基础打牢了,后面写驱动、调设备树的时候,就不会被环境问题绊住脚了。