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
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"
/proc、/sys、/dev 这三个挂载点,否则很多驱动和工具无法正常工作。
2.4 整体流程框架图
下面这张图展示了开发环境准备的整体流程,从工具链到内核再到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提供运行环境,工具链则是连接这一切的桥梁。把这一步走稳,后面的路就好走了。