2. 开发环境准备:交叉编译工具链安装、内核源码树配置、Busybox构建最小Rootfs

做嵌入式开发,环境准备是第一步,也是最容易踩坑的一步。我记得刚入行那会儿,光搭环境就折腾了两天,最后发现是工具链版本跟内核不匹配。这节课,我把这些年积累的经验梳理出来,帮你把环境一次搭对。

2.1 交叉编译工具链安装

说白了,交叉编译就是在你的PC上编译出能在ARM(或其他架构)板子上跑的程序。为什么不能直接在板子上编译?板子资源有限,跑个GCC能把内存吃光。

2.1.1 工具链的选择

我个人习惯用Linaro提供的工具链,稳定且社区活跃。当然你也可以用Buildroot或Yocto自己构建,但前期学习阶段没必要。

工具链类型 适用场景 下载地址示例
arm-linux-gnueabihf ARM 32位,硬件浮点 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf
aarch64-linux-gnu ARM 64位 gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu

2.1.2 安装步骤

下载后解压到固定目录,我一般放在 /opt/toolchains/ 下。

# 解压
tar -xf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/toolchains/

# 添加环境变量(写入 ~/.bashrc)
export PATH=/opt/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

# 验证
arm-linux-gnueabihf-gcc --version
注意: 我曾经遇到过PATH路径写错,导致编译时调用了宿主机的gcc,编译出来的程序在板子上跑不起来。建议用 which arm-linux-gnueabihf-gcc 确认路径正确。

2.2 内核源码树配置

内核源码树是驱动开发的基础。你想想看,驱动要调用内核的API,没有源码树,编译时头文件都找不到。

2.2.1 获取内核源码

从kernel.org下载,或者用LTS版本。我个人推荐长期支持版,稳定。

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xf linux-5.10.100.tar.xz
cd linux-5.10.100

2.2.2 配置内核

配置内核有两种方式:

  • make defconfig:使用默认配置,适合快速开始
  • make menuconfig:图形化配置,适合定制

我建议先用defconfig生成基础配置,再根据需要调整。

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
# 或者
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
小技巧: 配置时记得开启 CONFIG_MODULES=y,否则你的驱动模块加载不进去。我在项目中就吃过这个亏,折腾了半天才发现是内核没开模块支持。

2.2.3 编译内核

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

编译完成后,你会得到 arch/arm/boot/zImage 和一堆 .ko 文件。嗯,这里要注意,设备树文件(dtbs)也要一并编译,很多新手会漏掉。

2.3 Busybox构建最小Rootfs

Rootfs是根文件系统,说白了就是板子启动后能看到的 /bin、/sbin、/etc 这些目录。Busybox是一个集成了上百个Linux命令的“瑞士军刀”,一个二进制文件就能提供 ls、cp、sh 等所有基础命令。

2.3.1 下载与配置Busybox

wget https://busybox.net/downloads/busybox-1.33.2.tar.bz2
tar -xf busybox-1.33.2.tar.bz2
cd busybox-1.33.2
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

在menuconfig中,记得开启 Settings -> Build static binary (no shared libs)。为什么?静态编译的Busybox不依赖动态库,放到任何板子上都能跑。我曾经在项目里用动态编译,结果板子上的glibc版本不对,命令全挂了。

2.3.2 编译与安装

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install CONFIG_PREFIX=../rootfs

安装完成后,../rootfs 目录下会出现 bin、sbin、usr 等目录。

2.3.3 完善Rootfs目录结构

Busybox只提供了命令,还需要手动创建一些目录和文件。

cd ../rootfs
mkdir -p proc sys dev etc/init.d lib/modules
touch etc/inittab
touch etc/init.d/rcS
chmod +x etc/init.d/rcS

etc/inittab 是init进程的配置文件,内容如下:

::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

etc/init.d/rcS 是系统初始化脚本:

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /dev
mdev -s
echo "Welcome to Minimal Rootfs"
核心要点: 最小Rootfs必须包含 /proc、/sys、/dev 这三个挂载点,否则很多驱动和工具无法正常工作。

2.4 整体流程框架图

下面这张图展示了开发环境准备的整体流程,从工具链到内核再到Rootfs,环环相扣。

开发环境准备流程框架图 开发环境准备流程框架图 交叉编译工具链 arm-linux-gnueabihf-gcc 内核源码树 linux-5.10.100 Busybox busybox-1.33.2 编译 编译 内核产物 zImage + .ko + dtbs 最小Rootfs /bin /sbin /etc /dev 驱动集成到Rootfs 内核模块 + 设备树 + 应用程序 说明:工具链是基础,内核和Busybox都依赖它编译;最终内核产物和Rootfs合并,形成可启动的系统

2.5 验证环境

环境搭好了,怎么验证?我一般用一个小驱动模块来测试。

# 编写一个简单的hello模块
cat > hello.c << EOF
#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_init(void) {
    printk(KERN_INFO "Hello, Driver World!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye, Driver World!\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
EOF

# 编写Makefile
cat > Makefile << EOF
obj-m := hello.o
KDIR := /path/to/linux-5.10.100
PWD := $(shell pwd)
all:
    make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
clean:
    make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
EOF

# 编译
make

# 检查生成的.ko文件
file hello.ko

如果 file hello.ko 显示 ELF 32-bit LSB relocatable, ARM,说明环境配置正确。

避坑指南: 我曾经在编译模块时忘记指定 ARCH=arm,结果编译出来的是x86架构的.ko文件,放到板子上insmod直接报错。每次编译前,确认环境变量 ARCH 和 CROSS_COMPILE 已正确设置。

好了,环境准备就到这里。工具链、内核、Busybox这三样东西,是后续所有工作的基石。你想想看,驱动编译需要内核头文件,Rootfs需要Busybox提供运行环境,工具链则是连接这一切的桥梁。把这一步走稳,后面的路就好走了。