前言
之前刚开始学习Linux驱动照着教程手动构建嵌入式Linux系统时,发现了很多教程里使用的U-Boot、Linux Kernel、BusyBox 过于老旧,比如正点原子的Linux驱动教程。这导致很多软件难以支持,无法利用很多新特性,于是头铁脱离这些老旧的教程,去网上寻找较新的教程进行手动构建。在这个过程中我收获很多,但也发现手动构建已经是"非物质文化遗产“了,如今有更方便快速的方法对Linux系统进行构建,于是在了解更先进的方法后趁热打铁写下了这篇文章。
手动构建
嵌入式 Linux 系统的构建,通常包括 Bootloader、Linux Kernel、设备树、根文件系统以及最终的镜像打包与烧录等几个部分。理论上,开发者可以分别获取厂商或社区提供的 U-Boot、Linux Kernel 和根文件系统源码,手动完成裁剪、配置、编译、移植与集成,从而搭建出一个能够在目标硬件平台上运行的最小系统。
这种“手动构建”的方式有助于理解嵌入式 Linux 系统从启动到运行的整体链路,例如 Bootloader 如何加载内核、设备树如何描述硬件、内核如何驱动外设,以及根文件系统如何为用户态程序提供运行环境。因此,它在学习和理解系统构成方面仍然具有较高价值。具体流程可参考下面的流程图。
+------------------------------+
| 明确目标硬件平台 |
| SoC/开发板/存储介质/启动方式 |
+-----------+------------------+
|
| 准备源码与工具链
v
+------------------------------+
| 获取交叉编译工具链与源码 |
| U-Boot / Kernel / BusyBox |
| 以及板级补丁和设备树 |
+---------------+--------------+
|
| 分别进行裁剪与配置
┌-------------------+---------------------------┐
| | |
v v v
+----------------------+ +----------------------+ +----------------------+
| 配置 U-Boot | | 配置 Linux 内核 | | 配置根文件系统 |
| board config/menu | | defconfig/menuconfig | | BusyBox / init 脚本 |
+----------+-----------+ +----------+-----------+ +----------+-----------+
| | |
| 编译 U-Boot | 编译内核、dtb、模块 | 安装用户态文件
v v v
+----------------------+ +----------------------+ +----------------------+
| 生成 bootloader 镜像 | | 生成 zImage/Image、 | | 生成根文件系统目录树 |
| u-boot.bin 等 | | dtb、modules | | /bin /sbin /lib 等 |
+----------+-----------+ +----------+-----------+ +----------+-----------+
| | |
| | 安装模块到 rootfs | 补充运行环境
| +------------+--------------+ 创建设备节点/库/配置
| |
| v
| +------------------------------+
| | 完善根文件系统内容 |
| | init / inittab / rcS / |
| | lib/modules / firmware |
| +--------------+---------------+
| |
| | 选择文件系统格式
| | ext4 / cpio / squashfs
| v
| +------------------------------+
| | 生成 rootfs 镜像或归档 |
| | rootfs.ext4 / initramfs.cpio |
| +--------------+---------------+
| |
+-------------------+---------------------+
|
| 整合启动所需文件
| bootloader + kernel + dtb + rootfs
v
+------------------------------+
| 打包与部署镜像 |
| 烧录 eMMC/SD/NAND/TFTP/NFS |
+--------------+---------------+
|
| 设置启动参数
| bootcmd / bootargs
v
+------------------------------+
| 板卡上电启动 |
| U-Boot 加载内核与设备树 |
+--------------+---------------+
|
| 内核启动并挂载根文件系统
v
+------------------------------+
| 用户空间启动 init |
| 驱动、服务、应用开始运行 |
+--------------+---------------+
|
+------------------+------------------+
| |
| 启动失败/驱动异常 | 启动成功
v v
+------------------------------+ +------------------------------+
| 串口日志分析并回退修改 | | 系统进入可用状态 |
| 调整配置、设备树、驱动、rootfs | | 后续进行测试与功能验证 |
+------------------------------+ +------------------------------+
不过在实际项目开发中,完全手动构建往往不是主流做法。原因在于,现代嵌入式开发通常更加依赖成熟的构建系统和发行方案,例如Buildroot、Yocto,或芯片厂商提供的 SDK。这些工具能够更高效地完成源码管理、依赖处理、补丁集成、镜像生成和版本维护,因此更适合工程化开发。
因此,手动构建在今天更多是一种学习方法,而不是大多数项目中的最终生产方案。本文提及这一过程,主要是为了建立对嵌入式 Linux 系统组成和构建流程的整体认识,具体细节在此不再展开。如果想要了解,可以阅读下面提及的参考文章。
Buildroot
Buildroot 是一个轻量化的工具(相比于下面提到的Yocto来说),可以帮助我们快速的构建 Bootloader、Linux Kernel 以及 rootfs 并打包。相比于手动构建,使用Buildroot构建一个能用的Linux系统在效率上差的不是一丁半点,十分的明显(不相信的话可以尝试一下手动构建,并移植常用的程序)。
Buildroot 本质上是一个 Makefile脚本集合,熟悉make menuconfig功能即可上手使用,不需要付出太多的学习成本即可掌握。
下载 Buildroot
去Buildroot官网的Download网页下载源码即可,建议下载LTS,也就是 Long-term support 版本。
一般为压缩包格式,比如 tar.gz,使用 tar 命令解压即可。
使用 Buildroot
Buildroot 的一般使用步骤
在 Buildroot 源码的根目录下执行 make menuconfig 即可配置我们最终想要的 Linux 系统。
一般来说,配置并编译的步骤如下:
- 配置 Target options,配置目标平台架构等内容。
- 配置 Toolchain,配置交叉编译工具链。
- 配置 System configuration,配置系统设置。
- 配置 Filesystem images,配置文件系统镜像。
- 配置 Linux Kernel 和 Bootloader,一般使用开发板厂家提供的内核与引导程序,所以这一步一般禁止。
- 配置 Target packages,配置需要移植那些软件包。
- 编译 buildroot,执行
sudo make编译出自制的Linux系统所需的所有文件。
最终编译并打包的产物在 output/images/ 目录下,主要关注这么几个文件:
- rootfs.tar,解压后即为 buildroot 编译出来的根文件系统,可以通过 nfs 等方式挂载到开发板上。
常用的脚本工具
sudo make show-targets,用于查看当前 buildroot 所有配置了的目标软件包,也就是 packages。
Yocto
#待补充
总结
#待补充
参考文章
- 正点原子Alpha IMX6ULL开发板2025年最新部署方案:基于Ubuntu24.04平台开发,部署最新的UBoot/Linux和BusyBox Rootfs部署指南
- 正点原子I.MAX6ULL kernel6.6移植遇到的问题
- Linux RTL8188EUS Wifi驱动调试 (这个可看可不看,主要作为上一个博客中wifi驱动的补充说明)
- 【正点原子】I.MX6U嵌入式Linux驱动开发指南V2.0.1