3、嵌入式Linux基础:交叉编译环境搭建、Bootloader、内核编译、根文件系统制作
好,咱们进入嵌入式Linux的核心环节。说实话,很多初学者一上来就被「交叉编译」「Bootloader」「内核」「根文件系统」这四个词给唬住了。我当年也一样,觉得这玩意儿高深莫测。但干过几个项目之后你会发现,说白了就是一套固定的流程,你照着走,走通了,系统就跑起来了。
这一章,我带你把这四个环节挨个捋一遍。每个环节我都会讲讲我踩过的坑,你注意避让就行。
3.1 交叉编译环境搭建:为什么不能在PC上直接编译?
先问个问题:你的开发电脑是x86架构,而目标板(比如ARM开发板)是ARM架构。你在x86上编译出来的二进制文件,ARM能认识吗?答案是不能。这就是交叉编译存在的理由——在一种架构上生成另一种架构能跑的程序。
我个人习惯用Linaro GCC工具链,稳定、社区活跃。搭建步骤其实很简单:
- 下载对应架构的交叉编译器(比如arm-linux-gnueabihf-)
- 解压到 /opt 目录下
- 配置环境变量 PATH
具体命令如下:
# 下载并解压
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
tar -xvf gcc-linaro-*.tar.xz -C /opt/
# 配置环境变量(建议写到 ~/.bashrc 里)
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
# 验证是否安装成功
arm-linux-gnueabihf-gcc --version
3.2 Bootloader:系统启动的第一段代码
Bootloader,说白了就是上电后第一个跑起来的程序。它的任务很简单:初始化硬件,加载内核到内存,然后跳过去执行。
嵌入式领域最常用的Bootloader是U-Boot(全称Das U-Boot)。我参与过的项目里,90%都在用U-Boot。它的配置和编译流程大致如下:
# 下载U-Boot源码
git clone https://github.com/u-boot/u-boot.git
cd u-boot
# 配置(以某款ARM开发板为例)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- <board_name>_defconfig
# 编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
编译完成后,你会得到一个 u-boot.bin 文件。这个文件需要烧写到板子的启动介质上,比如SD卡、NAND Flash或eMMC。
这里有个关键点:U-Boot的环境变量。你可以在U-Boot命令行里设置启动参数,比如内核从哪里加载、根文件系统挂载到哪里。我习惯把常用的环境变量写到 include/configs/ 目录下的头文件里,这样每次编译都自动带上了。
3.3 内核编译:裁剪与定制
Linux内核是个庞然大物,但嵌入式设备资源有限,你不能把整个内核都塞进去。所以,内核编译的核心就是裁剪——只保留你需要的功能。
编译内核的步骤其实不复杂:
# 下载内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xvf linux-5.10.100.tar.xz
cd linux-5.10.100
# 配置内核(基于默认配置进行修改)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- <board_defconfig>
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 编译内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
编译完成后,你会得到 arch/arm/boot/zImage(内核镜像)和一堆 .dtb 文件(设备树)。
关于设备树,我多说两句。设备树是用来描述硬件信息的,比如GPIO怎么配、I2C挂在哪里、中断号是多少。以前这些信息都硬编码在内核里,后来发现太死板了,就改成了设备树这种灵活的方式。你想想看,换一块板子,只要改改设备树,内核不用重新编译,多方便。
3.4 根文件系统制作:让系统有「家」
内核跑起来之后,需要挂载一个根文件系统(Root Filesystem)。没有它,系统就只是个空壳,什么应用都跑不了。
根文件系统里包含什么呢?说白了就是 /bin、/sbin、/etc、/lib、/usr 这些目录,以及里面的各种工具和库。
制作根文件系统,我推荐用 Buildroot 或 Yocto。Buildroot更轻量,适合初学者。Yocto功能更强大,但学习曲线陡峭一些。
用Buildroot制作根文件系统的流程:
# 下载Buildroot
git clone https://github.com/buildroot/buildroot.git
cd buildroot
# 配置(选择目标架构、工具链、软件包等)
make menuconfig
# 编译
make
# 编译完成后,output/images/ 目录下会生成:
# rootfs.tar(根文件系统压缩包)
# uImage(内核镜像,如果配置了的话)
# <board>.dtb(设备树)
生成的 rootfs.tar 可以解压到SD卡或NFS目录下,让内核挂载。
/dev、/proc、/sys 这三个目录。它们是系统运行的基础,分别对应设备节点、进程信息和内核参数。少了任何一个,系统都可能无法正常工作。
init 程序,结果内核启动后直接panic了。系统找不到第一个用户态进程,当然跑不起来。所以,记得检查 /sbin/init 或 /bin/sh 是否存在。
3.5 整体流程回顾
好,咱们把整个流程串起来看看:
| 步骤 | 产出物 | 说明 |
|---|---|---|
| 交叉编译环境搭建 | arm-linux-gnueabihf-gcc等工具 | 用于编译Bootloader、内核、应用 |
| Bootloader编译 | u-boot.bin | 负责初始化硬件、加载内核 |
| 内核编译 | zImage + dtb文件 | 系统核心,负责管理硬件和进程 |
| 根文件系统制作 | rootfs.tar 或 ext4镜像 | 提供用户空间环境和工具 |
你把这四样东西准备好,烧写到板子上,上电,系统就能跑起来了。嗯,听起来是不是没那么难了?
最后说一句:嵌入式Linux开发,说白了就是跟硬件打交道。你多动手、多试错,慢慢就能摸清门道。我当年也是从烧坏好几块板子开始的,别怕。