2. 嵌入式Linux基础
做嵌入式Linux开发,第一件事就是把环境搭好。我见过不少新手,一上来就急着写代码,结果编译报错、烧录失败,折腾半天发现是工具链没装对。说白了,环境搭建就像打地基,地基不稳,后面全是坑。
这一章,我们聚焦四个核心环节:交叉编译工具链、TFTP/NFS服务、U-Boot编译、内核编译。每个环节我都会结合项目中的实际经验来讲,尤其是那些容易踩坑的地方。
本章知识体系速览
交叉编译工具链 → TFTP/NFS服务 → U-Boot编译 → 内核编译 → 验证流程
2.1 交叉编译工具链安装
什么是交叉编译?说白了,就是在你的PC(x86架构)上编译出能在ARM板子上运行的代码。你想想看,工控网关的CPU通常是ARM Cortex-A系列,性能有限,不可能直接在板子上编译Linux内核。
我个人习惯用Linaro GCC工具链,稳定、社区活跃。以ARM Cortex-A7为例,我们选gcc-arm-linux-gnueabihf。
安装步骤
- 下载工具链:从Linaro官网或ARM官网获取。
- 解压到指定目录:建议放在
/opt/toolchains/下。 - 配置环境变量:将工具链的bin目录加入
PATH。 - 验证安装:运行
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和内核编译则是整个系统的核心。把这些基础打牢,后面做协议集成、驱动开发才能事半功倍。