2. 嵌入式Linux基础

说实话,很多刚入行的朋友觉得嵌入式Linux开发最难的是写驱动、调协议。但我个人的经验是——环境搭不好,后面全是坑。我在项目里见过太多人,花了两周调一个bug,最后发现是交叉编译链版本不对。嗯,咱们先把地基打牢。

这一章,我会带你走一遍完整的开发环境搭建流程。包括交叉编译工具链、TFTP/NFS服务、U-Boot和内核编译,还有根文件系统制作。你跟着做一遍,后面写代码、调板子会顺手很多。

本章核心目标: 在一台x86_64的Linux主机上,搭建出能编译ARM架构目标板(比如i.MX6ULL、STM32MP1)的完整开发环境。
嵌入式Linux开发环境架构 开发主机(x86_64 / Ubuntu 22.04) 交叉编译工具链 arm-linux-gnueabihf- TFTP 服务 /tftpboot NFS 服务 /nfsroot U-Boot 源码 编译出 u-boot.imx Linux 内核源码 编译出 zImage + dtb 根文件系统 Buildroot / Yocto 目标板(ARM Cortex-A7 / i.MX6ULL) U-Boot 引导加载程序 Linux 内核 zImage + dtb 根文件系统 ext4 / squashfs 应用程序 Modbus / OPC UA TFTP NFS 烧录/挂载 主机编译 → 网络传输 → 目标板运行,这就是嵌入式开发的经典工作流

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

你想想看,你的开发主机是x86架构,目标板是ARM架构。x86的机器编译出来的程序,ARM芯片根本跑不了。所以我们需要交叉编译工具链——在x86上编译出ARM能运行的二进制文件。

我个人习惯用Linaro提供的预编译工具链,稳定、省事。以i.MX6ULL为例,我们选gcc-arm-linux-gnueabihf。

安装步骤

  1. 下载工具链:从Linaro官网或ARM官方下载gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf.tar.xz
  2. 解压到指定目录:sudo tar -xvf gcc-arm-*.tar.xz -C /opt/
  3. 添加环境变量:编辑~/.bashrc,加入export PATH=$PATH:/opt/gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf/bin
  4. 验证安装:arm-linux-gnueabihf-gcc --version
小技巧: 我建议在/opt下建一个toolchains目录,把不同架构的工具链都放进去。以后换芯片平台,改个PATH就行,不用重新折腾。

2.2 TFTP与NFS服务:让板子“吃”到文件

板子刚上电,没有操作系统,怎么把内核和文件系统传进去?这时候就需要TFTP和NFS了。说白了,TFTP用来传内核镜像,NFS用来挂载根文件系统——这样你改代码后不用反复烧写SD卡,直接重启板子就能看到效果。

TFTP服务配置

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

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

# 修改配置文件 /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure -c -s"

# 重启服务
sudo systemctl restart tftpd-hpa

NFS服务配置

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

# 创建NFS共享目录
sudo mkdir -p /nfsroot
sudo chmod 777 /nfsroot

# 编辑 /etc/exports,添加一行
/nfsroot *(rw,sync,no_subtree_check,no_root_squash)

# 重启服务
sudo systemctl restart nfs-kernel-server
注意: 我曾经在调试时发现板子挂载NFS总是超时,查了半天发现是防火墙没关。如果你也遇到类似问题,先执行sudo ufw disable试试。

2.3 U-Boot编译:板子的“第一段代码”

U-Boot是板子上电后第一个跑的程序。它负责初始化硬件、加载内核。我一般从芯片厂商的官方仓库拉代码,比如NXP的imx_v2020.04分支。

编译步骤

# 下载U-Boot源码
git clone https://source.codeaurora.org/external/imx/uboot-imx
cd uboot-imx
git checkout imx_v2020.04

# 配置(以mx6ull_14x14_evk为例)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_defconfig

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

# 编译产物:u-boot.imx(带IVT头的镜像)
关键点: CROSS_COMPILE必须指定为你的交叉编译链前缀,不要漏掉后面的短横线。漏了的话,编译会调用本机的gcc,出来的东西板子跑不了。

2.4 Linux内核编译:定制你的系统核心

内核是操作系统的核心。对于工控网关,我们通常需要裁剪掉不需要的驱动,只保留网络、存储、串口等必要模块。这样内核小、启动快、也更安全。

编译步骤

# 下载内核源码
git clone https://source.codeaurora.org/external/imx/linux-imx
cd linux-imx
git checkout imx_5.4.70_2.3.0

# 配置内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig

# 如果需要微调,运行菜单配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

# 编译内核和设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 dtbs

# 编译产物:arch/arm/boot/zImage 和 arch/arm/boot/dts/*.dtb
经验之谈: 第一次编译内核时,menuconfig里上千个选项很容易让人懵。我的做法是:先用默认配置编译通过,确保环境没问题。然后再逐步裁剪。一步到位往往出问题。

2.5 根文件系统制作:Buildroot vs Yocto

根文件系统是Linux启动后挂载的“/”目录,里面放着所有用户态程序、库、配置文件。制作根文件系统,主流方案有两个:Buildroot和Yocto。

对比项 Buildroot Yocto
学习曲线 低,配置简单 高,概念多(Recipe、Layer、BitBake)
构建速度 快,单次编译 慢,但增量编译做得好
定制灵活性 中等,适合固定需求 极高,适合复杂产品
包管理 无,编译时全静态 支持RPM/DEB包生成
适用场景 原型验证、小批量产品 大规模量产、需要长期维护

Buildroot快速上手

# 下载Buildroot
wget https://buildroot.org/downloads/buildroot-2023.02.tar.gz
tar -xvf buildroot-2023.02.tar.gz
cd buildroot-2023.02

# 配置(选择目标芯片架构)
make menuconfig
# Target options -> Target Architecture = ARM (little endian)
# Target options -> Target Binary Format = ELF
# Target options -> Target Architecture Variant = cortex-A7
# Toolchain -> Toolchain type = External toolchain
# Toolchain -> Toolchain = Custom toolchain
# Toolchain -> Toolchain path = /opt/gcc-arm-10.3-2021.07-x86_64-arm-linux-gnueabihf
# System configuration -> Root filesystem overlay directory = /path/to/your/overlay

# 编译
make -j4

# 编译产物:output/images/rootfs.tar 或 rootfs.ext4
避坑指南: 我曾经在Buildroot里选了错误的ARM变体(比如选了cortex-A9但芯片是cortex-A7),编译出来的文件系统在板子上跑起来各种段错误。一定要确认芯片的CPU核心型号。

Yocto快速上手(简版)

# 下载Poky(Yocto参考发行版)
git clone git://git.yoctoproject.org/poky
cd poky
git checkout dunfell

# 初始化构建环境
source oe-init-build-env build

# 修改 conf/local.conf
MACHINE ??= "imx6ull-14x14-evk"
DL_DIR ?= "/path/to/downloads"
SSTATE_DIR ?= "/path/to/sstate-cache"

# 修改 conf/bblayers.conf,添加meta-freescale层
# 编译核心镜像
bitbake core-image-minimal

# 编译产物:tmp/deploy/images/imx6ull-14x14-evk/*
我的建议: 如果你是第一次做嵌入式Linux,先用Buildroot。它简单直接,半天就能跑起来。Yocto虽然强大,但第一次配置很容易被各种Layer依赖搞崩溃。等你对整个流程熟悉了,再上Yocto做产品化。

2.6 本章小结:你得到了什么?

走完这一章,你的开发主机应该具备了以下能力:

  • 能用arm-linux-gnueabihf-gcc编译ARM程序
  • TFTP服务已启动,板子能从主机下载内核镜像
  • NFS服务已启动,板子能挂载主机的文件系统
  • 能编译出U-Boot镜像(u-boot.imx)
  • 能编译出Linux内核(zImage + .dtb)
  • 能用Buildroot或Yocto制作根文件系统

这些工具和服务,就是你后面所有开发工作的基础。别嫌麻烦,每一步都亲手敲一遍。我当年第一次搭环境时,光NFS就折腾了一下午——但搞通之后,后面调驱动、跑应用就顺畅多了。