2. 嵌入式Linux开发基础

在进入 Yocto SDK 与交叉开发环境之前,必须先建立嵌入式 Linux 开发的基本认知框架。很多初学者第一次接触 Yocto 时,会把它当成一个“编译工具”,但实际上 Yocto 解决的是从主机到目标板的整套构建与交付问题。要理解它,先要理解嵌入式系统的组成、交叉编译的本质、工具链的角色,以及主机与目标板之间的关系。

一、嵌入式系统的组成

一个典型的嵌入式 Linux 系统,可以按“从上到下、从软件到硬件”的方式拆成若干层。最上层是应用程序,例如业务逻辑、网络服务、GUI 程序;其下是系统库与运行时,如 glibc/musl、libstdc++、OpenSSL 等;再往下是根文件系统,包含 /bin、/etc、/lib、/usr 等目录结构;然后是Linux 内核,负责进程调度、内存管理、设备驱动和文件系统;再往下是引导加载程序,如 U-Boot,负责初始化硬件并加载内核;最底层是硬件平台,包括 SoC、DDR、Flash、外设接口等。

这六层并不是孤立存在的,它们之间存在严格的依赖关系:应用依赖库,库依赖根文件系统,根文件系统依赖内核提供的系统调用,内核依赖 Bootloader 正确加载,Bootloader 依赖硬件初始化。Yocto 的价值,正是把这条链上的每一层都纳入可配置、可复现的构建流程中。

二、交叉编译:为什么不能直接在目标板上编译

嵌入式设备通常资源受限:CPU 主频低、内存小、存储空间有限,甚至没有完整的编译环境。如果直接在目标板上编译程序,会面临编译速度慢、依赖缺失、磁盘空间不足等问题。因此,嵌入式开发普遍采用交叉编译:在性能更强的主机(通常是 x86_64 PC)上,使用能够生成目标板架构代码的编译器,产出可在目标板上运行的可执行文件。

交叉编译的核心在于“三要素”匹配:架构(如 ARM、AArch64、RISC-V)、ABI(如 EABI、gnueabi、gnueabihf)和系统库(glibc 版本、动态链接器路径)。三者中任意一项不匹配,都会导致程序无法运行,常见现象是“No such file or directory”或“cannot execute binary file”。

交叉编译的典型命令形式如下,其中 --host 指定目标架构,--build 指定主机架构:
./configure --host=arm-poky-linux-gnueabi \
            --build=x86_64-linux \
            --prefix=/usr \
            --with-sysroot=$SDKTARGETSYSROOT
make
make install DESTDIR=$D

三、工具链的角色

工具链(Toolchain)是交叉编译的执行主体,它并不是一个单一程序,而是一组协同工作的工具集合。常见成员包括:

  • 编译器:如 arm-poky-linux-gnueabi-gcc,负责将 C/C++ 源码编译为目标架构汇编或目标文件。
  • 汇编器:如 as,将汇编代码转换为机器码目标文件。
  • 链接器:如 ld,将多个目标文件和库链接为可执行文件或共享库。
  • 二进制工具:如 objcopy、objdump、strip、readelf,用于格式转换、反汇编、裁剪和查看 ELF 信息。
  • C 库与头文件:提供目标板运行所需的系统调用封装和标准库实现。
  • sysroot:目标板根文件系统的“镜像目录”,包含头文件、库和 pkg-config 文件,是交叉编译时查找依赖的依据。

在 Yocto 中,工具链通常以 SDK 的形式交付。安装 SDK 后,执行 source environment-setup-* 会设置 CC、CXX、LD、PKG_CONFIG_PATH、SDKTARGETSYSROOT 等环境变量,使后续的 configure/make 自动使用正确的交叉工具。

四、目标板与主机的关系

主机(Host)是开发用的 PC,负责编写代码、编译、打包和调试;目标板(Target)是最终运行嵌入式系统的硬件。两者之间通过多种通道协作:

通道典型接口用途
串口UART / USB-TTL查看启动日志、进入 U-Boot、登录控制台
网络以太网 / Wi-FiNFS 挂载根文件系统、SSH 登录、传输文件
调试JTAG / SWD裸机或内核早期调试、烧录 Bootloader
存储SD 卡 / USB / TFTP烧写镜像、更新固件

主机与目标板的关系可以概括为“主机生产,目标板消费”。主机上生成的镜像、库、应用,最终都要通过上述通道部署到目标板。Yocto 构建出的 SDK 和镜像,正是连接这两端的桥梁。

主机与目标板交叉开发关系图 主机 Host (x86_64 PC) 源代码 / 工程文件 Yocto 构建系统 (BitBake) 交叉工具链 + SDK 镜像 / 根文件系统 / 应用 调试器 / 串口终端 / SSH 目标板 Target (ARM/AArch64) Bootloader (U-Boot) Linux 内核 + 设备树 根文件系统 (rootfs) 应用程序 / 服务 硬件平台 (SoC / DDR / Flash) 交叉工具链 gcc / ld / as sysroot / 库 objcopy / strip 镜像部署 / 调试通道 编译产物 运行环境

五、常见误区与复盘

误区一:把交叉编译当成“换个编译器”。 实际上,交叉编译要求头文件、库、链接器脚本、sysroot 全部匹配目标板。只换 gcc 前缀,往往在链接阶段报“找不到 -lc”或“incompatible ABI”。

误区二:在主机上运行目标板程序。 交叉编译出的 ELF 文件无法在 x86_64 主机上直接执行,必须通过 QEMU 或部署到目标板运行。

误区三:忽略 sysroot 的版本一致性。 如果 SDK 的 sysroot 与目标板实际根文件系统版本不一致,程序可能在目标板上因动态库版本不匹配而崩溃。
复盘建议: 每次交叉编译前,先确认三件事——目标架构是什么、SDK 环境变量是否已 source、sysroot 路径是否正确。可以用 file 和 readelf -h 检查生成的 ELF 文件架构,用 ldd 检查动态依赖是否能在目标板满足。

理解主机与目标板的分工、交叉编译的约束以及工具链的组成,是后续使用 Yocto 构建 SDK、定制镜像和部署应用的基础。只有把这条“主机生产、目标板消费”的链路看清楚,才能在遇到编译错误、运行失败或依赖缺失时,快速定位问题发生在哪一层。