2. 开发环境搭建:交叉编译工具链安装、QEMU模拟器配置、Busybox根文件系统制作、最小化系统启动验证

做嵌入式Linux开发,第一关就是搭环境。很多人觉得这事简单,照着教程敲命令就行。但我见过太多人在这一步卡住——工具链版本不对、QEMU模拟器跑不起来、根文件系统缺这缺那。说白了,环境没搭好,后面全是白费功夫。

这一章,我带你从头到尾把环境捋一遍。咱们的目标很明确:用QEMU模拟一个ARM开发板,跑起一个最小化的Linux系统。整个过程我会穿插一些实际项目中的经验,希望能帮你少走弯路。

本章核心产出: 一个能在QEMU上启动的、包含Busybox根文件系统的最小Linux内核镜像。

2.1 整体流程概览

先看一张图,把整个搭建流程串起来。这样你心里有个谱,知道每一步在干什么。

开发环境搭建流程图 开发环境搭建核心流程 ① 交叉编译工具链 arm-linux-gnueabihf- ② 内核编译 zImage / uImage ③ 根文件系统 Busybox + 必要库 ④ QEMU模拟启动验证 -kernel + -initrd + -append 💡 关键点 • 工具链必须与目标架构匹配(ARM v7) • 内核配置要开启对QEMU虚拟硬件的支持 • 根文件系统要包含init程序,否则内核会panic

流程不复杂,但每一步都有坑。咱们一个一个来。

2.2 交叉编译工具链安装

交叉编译,说白了就是在你的x86电脑上,编译出能在ARM芯片上跑的程序。工具链就是干这个的。

2.2.1 选择工具链

我个人习惯用Linaro提供的工具链,稳定、更新及时。咱们的目标架构是ARM v7,所以选 arm-linux-gnueabihf 这个版本。

工具链版本 适用架构 浮点支持 我的推荐
arm-linux-gnueabi ARM v5/v6 软浮点 不推荐,太老
arm-linux-gnueabihf ARM v7 硬浮点 ✅ 推荐
aarch64-linux-gnu ARM v8 64位 硬浮点 看具体项目

2.2.2 安装步骤

我建议直接下载预编译的二进制包,省去自己编译的痛苦。具体操作:

# 下载Linaro工具链(以7.5-2019.12为例)
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
小技巧: 我习惯在项目根目录下创建一个 setenv.sh 脚本,专门用来设置工具链路径。这样不同项目可以用不同版本的工具链,互不干扰。

2.2.3 验证工具链

写一个简单的C程序测试一下:

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

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

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

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

看到 ARM 字样,说明工具链工作正常。嗯,这一步很简单,但千万别跳过。

2.3 QEMU模拟器配置

QEMU是个好东西,能模拟各种硬件。咱们用它来模拟一个ARM开发板,省得买实体板子。

2.3.1 安装QEMU

Ubuntu系统下直接apt安装:

sudo apt-get update
sudo apt-get install qemu-system-arm qemu-utils

验证安装:

qemu-system-arm --version

2.3.2 选择模拟的机器类型

QEMU支持很多ARM机器。我推荐用 vexpress-a9,它是ARM Versatile Express开发板的模拟,支持Cortex-A9多核处理器,跟真实项目比较接近。

机器类型 CPU 内存 适用场景
versatilepb ARM926EJ-S 256MB 入门测试
vexpress-a9 Cortex-A9 1GB ✅ 推荐
virt Cortex-A15 可配置 高级特性测试
注意: 不同机器类型对应的设备树文件(.dtb)不同。我曾经因为用了错误的dtb,导致内核启动到一半就挂了,查了半天才发现是设备树不匹配。

2.4 Busybox根文件系统制作

根文件系统是Linux启动后挂载的第一个文件系统。Busybox把很多常用命令打包成一个可执行文件,体积小,特别适合嵌入式系统。

2.4.1 下载并配置Busybox

# 下载Busybox
wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
tar -xvf busybox-1.36.0.tar.bz2
cd busybox-1.36.0

# 配置为静态编译(避免动态库依赖)
make menuconfig
# 进入 Settings → Build Options → 选中 "Build Busybox as a static binary (no shared libs)"

为什么要静态编译?你想想看,根文件系统里如果缺了动态库,busybox就跑不起来。静态编译把所有依赖都打包进去,省心。

2.4.2 编译并安装

# 指定交叉编译器和安装路径
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

# 安装到指定目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install CONFIG_PREFIX=../rootfs

2.4.3 构建根文件系统目录结构

Busybox安装完后,还需要手动创建一些必要的目录和文件:

cd ../rootfs
mkdir -p proc sys dev etc/init.d lib

# 创建init脚本
cat > etc/init.d/rcS << "EOF"
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
echo "Welcome to Minimal Embedded Linux!"
EOF

chmod +x etc/init.d/rcS

# 创建inittab
cat > etc/inittab << "EOF"
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
EOF
关键点: 根文件系统里必须有一个 init 程序。内核启动后会执行它。如果没有,内核会panic。Busybox安装后会在 sbin/init 生成这个程序。

2.4.4 制作根文件系统镜像

# 制作cpio格式的initrd
cd rootfs
find . | cpio -o --format=newc > ../rootfs.cpio
gzip -c ../rootfs.cpio > ../rootfs.cpio.gz

这里用cpio格式,是因为QEMU可以直接用 -initrd 参数加载它,方便调试。

2.5 内核编译与最小化配置

内核是系统的核心。咱们要编译一个针对vexpress-a9的最小化内核。

2.5.1 下载内核源码

wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.38.tar.xz
tar -xvf linux-6.1.38.tar.xz
cd linux-6.1.38

2.5.2 配置内核

我建议从vexpress的默认配置开始,然后裁剪:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig

# 进入菜单配置,进行裁剪
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

需要确保开启以下选项:

  • Device Drivers → Block devices → RAM block device support (用于initrd)
  • Device Drivers → Character devices → Serial drivers → ARM AMBA PL011 serial port support (串口输出)
  • File systems → <*> Ext2/ext3/ext4 file systems support (如果根文件系统用ext格式)

2.5.3 编译内核

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage -j4
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

编译完成后,在 arch/arm/boot/ 目录下会生成 zImage 和 dts/vexpress-v2p-ca9.dtb。

2.6 最小化系统启动验证

所有材料都准备好了,现在来验证一下能不能启动。

2.6.1 启动QEMU

qemu-system-arm \
  -M vexpress-a9 \
  -m 512M \
  -kernel /path/to/linux-6.1.38/arch/arm/boot/zImage \
  -dtb /path/to/linux-6.1.38/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
  -initrd /path/to/rootfs.cpio.gz \
  -append "console=ttyAMA0,115200 root=/dev/ram0 rdinit=/sbin/init" \
  -nographic

参数说明:

参数 说明
-M vexpress-a9 指定模拟的机器类型
-m 512M 分配512MB内存
-kernel 内核镜像路径
-dtb 设备树文件路径
-initrd 根文件系统镜像
-append 内核启动参数
-nographic 不使用图形界面,用终端输出

2.6.2 预期输出

如果一切顺利,你会看到类似下面的输出:

...
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 6.1.38 ...
...
[    1.234567] Freeing unused kernel memory: 1024K
[    1.240000] Run /sbin/init as init process
[    1.250000] mount: mounting proc on /proc succeeded
[    1.260000] mount: mounting sysfs on /sys succeeded
[    1.270000] Welcome to Minimal Embedded Linux!
/ # _

看到 / # 提示符,说明系统启动成功了!你现在就在一个最小的嵌入式Linux系统里。

常见问题:
  • 内核panic: 通常是根文件系统没找到或init程序缺失。检查 -initrd 路径和 rdinit 参数。
  • 串口无输出: 检查 console=ttyAMA0 是否匹配QEMU模拟的串口。vexpress-a9的串口就是ttyAMA0。
  • 设备树错误: 确保dtb文件与机器类型匹配。vexpress-a9对应 vexpress-v2p-ca9.dtb。

2.7 本章小结

到这里,咱们已经把开发环境搭起来了。你手头应该有了:

  • 一个能用的交叉编译工具链
  • 一个配置好的QEMU模拟器
  • 一个用Busybox制作的根文件系统
  • 一个能启动的最小化Linux内核

这个环境虽然简单,但五脏俱全。后面章节的内核裁剪、实时性优化,都要在这个基础上进行。我个人建议你把整个流程多走几遍,直到能闭着眼搭出来为止。熟能生巧,这话在嵌入式开发里特别适用。

我的习惯: 我会把整个环境搭建过程写成一个自动化脚本,放在项目的 scripts/ 目录下。这样换机器或者给同事用,一行命令就能复现环境。你也试试?