2. 嵌入式Linux基础

做嵌入式Linux开发,第一件事就是把环境搭好。我见过不少新手,一上来就急着写代码,结果编译报错、烧录失败,折腾半天发现是工具链没装对。说白了,环境搭建就像打地基,地基不稳,后面全是坑。

这一章,我们聚焦四个核心环节:交叉编译工具链、TFTP/NFS服务、U-Boot编译、内核编译。每个环节我都会结合项目中的实际经验来讲,尤其是那些容易踩坑的地方。

本章知识体系速览

交叉编译工具链 → TFTP/NFS服务 → U-Boot编译 → 内核编译 → 验证流程

嵌入式Linux开发环境搭建核心流程 交叉编译工具链 TFTP / NFS 服务 U-Boot 编译 Linux 内核编译 💡 关键依赖关系: 工具链是基础 → TFTP/NFS用于网络加载 → U-Boot引导内核 → 内核启动根文件系统

2.1 交叉编译工具链安装

什么是交叉编译?说白了,就是在你的PC(x86架构)上编译出能在ARM板子上运行的代码。你想想看,工控网关的CPU通常是ARM Cortex-A系列,性能有限,不可能直接在板子上编译Linux内核。

我个人习惯用Linaro GCC工具链,稳定、社区活跃。以ARM Cortex-A7为例,我们选gcc-arm-linux-gnueabihf。

安装步骤

  1. 下载工具链:从Linaro官网或ARM官网获取。
  2. 解压到指定目录:建议放在/opt/toolchains/下。
  3. 配置环境变量:将工具链的bin目录加入PATH。
  4. 验证安装:运行arm-linux-gnueabihf-gcc --version。
# 下载(以gcc-linaro-7.5.0为例)
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/toolchains/

# 配置环境变量(写入 ~/.bashrc)
export PATH=$PATH:/opt/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin

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

⚠️ 我曾经踩过的坑:工具链版本与内核版本不匹配。比如用太新的gcc编译旧版内核,会报一堆语法错误。建议内核版本与工具链大版本对齐,比如Linux 4.x系列用gcc 7.x,Linux 5.x系列用gcc 9.x。

测试交叉编译

写一个简单的Hello World,交叉编译后放到板子上运行,验证工具链是否正常工作。

// hello.c
#include <stdio.h>

int main() {
    printf("Hello, Embedded Linux!\n");
    return 0;
}

// 交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c

// 查看文件信息
file hello
// 输出:hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)

2.2 TFTP/NFS服务配置

开发阶段,频繁烧写Flash太慢了。我一般用TFTP下载内核和设备树,用NFS挂载根文件系统。这样改完代码,重启板子就能看到效果,效率翻倍。

TFTP服务配置

TFTP用于U-Boot阶段从PC下载内核镜像和设备树文件。

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

# 配置文件 /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"        # 文件存放目录
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure -c -s"

# 创建目录并设置权限
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp

# 重启服务
sudo systemctl restart tftpd-hpa
sudo systemctl enable tftpd-hpa

💡 小技巧:把编译好的zImage和.dtb文件复制到/srv/tftp目录。U-Boot端用tftp 0x82000000 zImage就能下载到内存。

NFS服务配置

NFS让板子通过网络挂载PC上的目录作为根文件系统,省去每次烧写Flash的麻烦。

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

# 配置文件 /etc/exports
/home/user/nfsroot  *(rw,sync,no_subtree_check,no_root_squash)

# 创建根文件系统目录
mkdir -p /home/user/nfsroot

# 导出并重启服务
sudo exportfs -a
sudo systemctl restart nfs-kernel-server

⚠️ 注意:no_root_squash选项很重要。不加的话,板子上的root用户会被映射成nobody,导致权限问题。我曾经因为这个排查了半天,最后发现是NFS权限配置漏了。

2.3 U-Boot编译基础

U-Boot是嵌入式Linux的引导加载程序。说白了,它负责初始化硬件、加载内核到内存、然后跳转执行。工控网关的U-Boot通常需要根据具体板卡配置。

获取源码与配置

# 下载U-Boot源码
git clone https://github.com/u-boot/u-boot.git
cd u-boot

# 查看支持的板卡配置
ls configs/ | grep your_board

# 以某款ARM工控板为例
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- your_board_defconfig

# 编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

关键配置项

配置项 说明 典型值
CONFIG_BOOTCOMMAND 自动启动命令 tftp 0x82000000 zImage; bootz 0x82000000
CONFIG_ETHADDR MAC地址 00:11:22:33:44:55
CONFIG_BOOTARGS 内核启动参数 console=ttyS0,115200 root=/dev/nfs
CONFIG_BAUDRATE 串口波特率 115200

🔑 我的经验:工控网关通常有双网口冗余需求。在U-Boot中要确保两个网口的MAC地址不同,且与内核设备树中的配置一致。否则网络切换时会出问题。

2.4 Linux内核编译基础

内核编译是嵌入式开发的核心环节。工控网关需要裁剪内核,去掉不需要的驱动,加入工业协议相关的模块(比如CAN、Modbus等)。

获取内核源码

# 从kernel.org下载稳定版
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

配置内核

配置方式有三种:make menuconfig(图形化)、make defconfig(默认)、make ARCH=arm your_defconfig(板级)。

# 设置架构和交叉编译器
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# 使用板级默认配置
make your_board_defconfig

# 或者手动配置
make menuconfig

编译内核

# 编译内核镜像
make zImage -j4

# 编译设备树
make dtbs

# 编译内核模块
make modules -j4

# 安装模块到根文件系统目录
make modules_install INSTALL_MOD_PATH=/home/user/nfsroot

💡 提速技巧:第一次编译用-j4(4线程),后续增量编译用make zImage -j4只编译修改的部分。我习惯把ARCH和CROSS_COMPILE写入~/.bashrc,省得每次敲。

编译产物说明

文件 路径 用途
zImage arch/arm/boot/ 压缩后的内核镜像,U-Boot加载
*.dtb arch/arm/boot/dts/ 设备树二进制文件,描述硬件
*.ko drivers/下各目录 内核模块,动态加载
vmlinux 源码根目录 未压缩的内核,用于调试

2.5 环境验证与常见问题

环境搭好后,我建议做一次完整的冷启动验证:从U-Boot通过TFTP下载内核,通过NFS挂载根文件系统,直到系统启动到shell。

U-Boot端启动命令示例

# 设置网络
setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10

# 下载内核和设备树
tftp 0x82000000 zImage
tftp 0x83000000 your_board.dtb

# 设置内核启动参数
setenv bootargs console=ttyS0,115200 root=/dev/nfs nfsroot=192.168.1.10:/home/user/nfsroot rw ip=192.168.1.100

# 启动内核
bootz 0x82000000 - 0x83000000

⚠️ 常见问题排查:

  • TFTP超时:检查防火墙是否开放69端口,确认文件路径和权限。
  • NFS挂载失败:检查/etc/exports语法,确认板子IP在允许范围内。
  • 内核panic:通常是设备树与内核版本不匹配,或者根文件系统路径错误。
  • 串口无输出:检查U-Boot和内核的波特率是否一致,我遇到过好几次115200和57600不匹配的情况。

📌 本章小结

环境搭建是嵌入式Linux开发的起点,也是后续所有工作的基础。交叉编译工具链让你能在PC上编译ARM代码,TFTP/NFS让开发迭代变得高效,U-Boot和内核编译则是整个系统的核心。把这些基础打牢,后面做协议集成、驱动开发才能事半功倍。