2. 开发环境搭建:交叉编译工具链安装、QEMU模拟器配置、Busybox根文件系统制作、最小化系统启动验证
做嵌入式Linux开发,第一关就是搭环境。很多人觉得这事简单,照着教程敲命令就行。但我见过太多人在这一步卡住——工具链版本不对、QEMU模拟器跑不起来、根文件系统缺这缺那。说白了,环境没搭好,后面全是白费功夫。
这一章,我带你从头到尾把环境捋一遍。咱们的目标很明确:用QEMU模拟一个ARM开发板,跑起一个最小化的Linux系统。整个过程我会穿插一些实际项目中的经验,希望能帮你少走弯路。
2.1 整体流程概览
先看一张图,把整个搭建流程串起来。这样你心里有个谱,知道每一步在干什么。
流程不复杂,但每一步都有坑。咱们一个一个来。
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 | 可配置 | 高级特性测试 |
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/ 目录下。这样换机器或者给同事用,一行命令就能复现环境。你也试试?