news 2026/4/17 9:14:20

完整示例:构建最小化ARM64可引导镜像文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
完整示例:构建最小化ARM64可引导镜像文件

从零构建一个能跑起来的ARM64最小系统:不只是“Hello World”

你有没有试过,只用几MB甚至更少的空间,让一块ARM64开发板真正启动起来?不是刷个现成镜像完事,而是亲手把代码、内核和根文件系统一砖一瓦地搭出来——这不仅是嵌入式工程师的基本功,更是理解操作系统如何“活过来”的最佳路径。

在工业控制、边缘网关、车载设备这些对资源敏感的场景里,标准Linux发行版动辄几百MB,启动几十秒,显然不现实。我们需要的是一个精简到骨子里的可引导系统:它体积小、启动快、攻击面窄,还能完全掌控每一个字节。

本文就带你一步步构建这样一个最小化ARM64可引导镜像——不讲空话,不堆术语,只聚焦一件事:怎么让它真正跑起来,并且你知道它是怎么跑的


先搞清楚:我们到底在做什么?

想象一下,当你按下电源键,SoC里的Boot ROM开始执行第一条指令,然后加载U-Boot,再由U-Boot把内核拉进内存,最后跳转过去……整个过程就像一场接力赛。

而我们要做的,就是为这场“接力”准备每一棒的内容:

  1. 交叉编译工具链—— 在x86主机上生成arm64代码;
  2. 裁剪过的Linux内核—— 只保留最基础的功能;
  3. 极简根文件系统(initramfs)—— 没有bash、没有systemd,只有一个/init脚本;
  4. 设备树(DTB)—— 告诉内核硬件长什么样;
  5. U-Boot引导程序—— 完成初始化并启动内核;
  6. 最终打包成单一镜像—— 写入SD卡或Flash即可运行。

全程无需外部存储挂载,也不依赖网络,一切都在内存中完成。目标是:上电 → 打印一行“Hello”,然后停在那里——但你知道它已经是一个完整的Linux系统了


工具准备:先建好厨房,再谈做饭

所有操作都在一台Ubuntu主机上进行(推荐20.04+),第一步永远是装好交叉编译工具链:

sudo apt install -y \ gcc-aarch64-linux-gnu \ make git cpio \ device-tree-compiler \ u-boot-tools

这条命令装齐了你需要的一切:
-aarch64-linux-gnu-gcc:用来编译arm64代码;
-makegit:基本构建工具;
-cpio:打包initramfs;
-dtc:编译设备树源文件(.dts → .dtb);
-mkimage:给U-Boot镜像签名。

⚠️ 注意:不要混用不同版本的工具链。如果你追求一致性,建议使用Linaro官方发布的GCC工具链,或者直接基于Buildroot/Yocto环境构建。


第一步:编译一个“够用就行”的Linux内核

我们不需要USB、蓝牙、图形界面,甚至连模块都不需要加载。要的就是一个静态链接、压缩后能被U-Boot直接启动的镜像。

获取内核源码

git clone https://github.com/torvalds/linux.git cd linux

切换到稳定分支(比如v6.6)会更稳妥,但主线也完全可以:

git checkout v6.6

配置内核:从defconfig开始裁剪

先用树莓派4的默认配置打底(兼容性好,文档全):

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig

接着进入可视化菜单进一步裁剪:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

关键裁剪项如下:

关闭项说明
Device Drivers → USB support不接U盘键盘鼠标
Device Drivers → Graphics support没有显示器
File systems → DOS/FAT/NTFS不读TF卡Windows分区
Cryptographic API除非做加密启动,否则关掉省空间
Kernel hacking → Debug info调试符号极大膨胀体积,发布时务必关闭

必须开启的关键选项

CONFIG_EMBEDDED=y CONFIG_BLK_DEV_INITRD=y CONFIG_DEVTMPFS=y CONFIG_ARCH_BCM2835=y # 树莓派4专用 CONFIG_ARM64_VA_BITS_48=y CONFIG_CMDLINE="console=ttyAMA0,115200 earlyprintk root=/dev/ram"

特别是最后一行CMDLINE,它决定了内核启动参数——串口输出、early打印、根文件系统位置,缺一不可。

开始编译

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image.gz dtbs

完成后你会得到两个核心产物:
-arch/arm64/boot/Image.gz:压缩后的内核镜像;
-arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb:树莓派4的设备树二进制。

记住它们的位置,后面要用。


第二步:做一个“只有/init”的根文件系统

真正的“最小系统”连BusyBox都不需要。我们可以手动写一个/init脚本,让它干完事就停下来。

创建目录结构

mkdir initramfs cd initramfs mkdir {bin,sbin,etc,proc,sys,dev}

编写/init脚本

cat > init << 'EOF' #!/bin/sh # 挂载必要的伪文件系统 mount -t proc none /proc mount -t sysfs none /sys mount -t devtmpfs none /dev echo "" echo "👋 Welcome to Minimal ARM64 Linux" echo "Kernel: $(uname -r)" echo "Time: $(date)" echo "" # 启动shell,如果没有,则循环防止退出 if [ -x /sbin/init ]; then exec /sbin/init else echo "No /sbin/init. Dropping to shell..." exec sh fi EOF chmod +x init

这个脚本做了三件事:
1. 挂载/proc,/sys,/dev—— 这是几乎所有程序运行的基础;
2. 输出欢迎信息;
3. 尝试执行/sbin/init,失败则降级到shell。

即使你不放任何用户程序,系统也不会崩溃。

打包成initramfs

find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz

现在你有了一个纯内存运行的根文件系统。下一步就是把它塞进内核。

把initramfs嵌入内核

回到内核源码目录,打开.config文件,添加或修改这一行:

CONFIG_INITRAMFS_SOURCE="../initramfs.cpio.gz"

然后重新编译一次内核:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image.gz

这次生成的Image.gz已经自带根文件系统了,U-Boot只要加载它,就能一键启动整个系统!


第三步:配置U-Boot,让它自动跑起来

U-Boot是那个“唤醒内核的人”。我们需要它完成以下任务:

  • 初始化DRAM;
  • 设置串口以便看到输出;
  • 加载内核和DTB到指定地址;
  • 自动执行启动命令,无需人工干预。

下载U-Boot源码

git clone https://source.denx.de/u-boot/u-boot.git cd u-boot git checkout v2024.01 # 推荐稳定版本

使用官方defconfig

以树莓派4为例:

make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- rpi_4_defconfig

修改自动启动命令

编辑include/configs/rpi.h或通过Kconfig设置:

#define CONFIG_BOOTCOMMAND \ "setenv bootargs console=ttyAMA0,115200 earlyprintk root=/dev/ram;" \ "load mmc 0:1 ${kernel_addr_r} Image.gz;" \ "load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb;" \ "fdt addr ${fdt_addr_r};" \ "booti ${kernel_addr_r} - ${fdt_addr_r};"

解释一下这几个变量:
-${kernel_addr_r}=0x80080000:内核解压后存放地址;
-${fdt_addr_r}=0x80000000:设备树加载地址;
-mmc 0:1表示从SD卡第一个分区读取文件。

你也可以把这些写成U-Boot脚本保存在环境变量中,但我们这里追求极致简单,直接硬编码。

编译U-Boot

make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

生成的主要文件:
-u-boot.bin:主镜像;
- 若启用SPL,还有spl/u-boot-spl.bin

对于树莓派4,可以直接将u-boot.bin放在FAT格式的SD卡根目录,配合config.txt引导即可。


最终整合:做一个能烧录的完整镜像

到现在为止,我们有三个关键组件:
1. U-Boot(放在启动分区)
2. 内核镜像Image.gz
3. 设备树bcm2711-rpi-4-b.dtb

可以把它们打包成一个完整的磁盘镜像,方便批量部署。

创建一个16MB的空白镜像

dd if=/dev/zero of=system.img bs=1M count=16

分区:第一区放U-Boot和内核,第二区备用

parted system.img mklabel msdos parted system.img mkpart primary fat32 1MiB 16MiB parted system.img set 1 boot on

格式化并挂载

mkfs.vfat system.img.part1 sudo losetup -Pf --show system.img # 映射为loop设备 sudo mount /dev/loop0p1 /mnt

复制文件

sudo cp u-boot.bin /mnt/ sudo cp linux/arch/arm64/boot/Image.gz /mnt/ sudo cp linux/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /mnt/

写入U-Boot到镜像开头(偏移0)

dd if=u-boot.bin of=system.img conv=notrunc seek=1

这样U-Boot SPL就能在通电时正确加载它。

卸载并清理

sudo umount /mnt sudo losetup -d /dev/loop0

现在你可以用dd把这个system.img写入SD卡:

sudo dd if=system.img of=/dev/sdX bs=1M status=progress

插入树莓派4,串口连上,通电——你应该能看到熟悉的U-Boot提示符,然后迅速跳转到Linux,打出那句“Welcome”。


常见坑点与调试秘籍

别以为做完就能一次成功。以下是我在实际调试中踩过的坑:

❌ 串口没输出?检查这几项:

  • 串口线是否接对(TX→RX,GND共地);
  • 波特率是否一致(通常是115200);
  • 内核命令行是否有console=参数
  • U-Boot是否启用了串口驱动CONFIG_DEBUG_UART有时能救命)。

❌ 内核卡住不动?

查看U-Boot传参:

=> printenv bootargs

确认是console=ttyAMA0,115200而不是ttyS0或其他名字。

❌ 提示“No filesystem could mount root”?

说明initramfs没生效。检查:
-CONFIG_INITRAMFS_SOURCE是否指向正确的.cpio.gz
- 是否重新编译了内核;
- 文件路径是否绝对/相对错误。

❌ U-Boot无法加载Image.gz?

注意:有些旧版U-Boot不支持gzip压缩的Image,需使用mkimage封装:

mkimage -A arm64 -O linux -T kernel -C gzip \ -a 0x80080000 -e 0x80080000 \ -d Image.gz uImage

然后U-Boot改用bootm命令加载uImage

不过现代U-Boot普遍支持booti直接解压启动,推荐优先使用。


进阶思路:这个最小系统还能怎么玩?

一旦你能让系统跑起来,接下来的扩展空间非常大:

✅ 替换/init为 C 程序

写个简单的C程序作为init:

#include <stdio.h> #include <unistd.h> int main() { printf("Custom init running...\n"); while(1) pause(); }

静态编译后放进initramfs,取代shell脚本,体积更小,行为更可控。

✅ 集成轻量守护进程

加入一个微型HTTP服务器(如mongoose)、Modbus客户端或MQTT上报器,瞬间变成物联网终端原型。

✅ 启用安全启动

结合Arm Trusted Firmware(ATF)和U-Boot Secure Boot,实现镜像签名验证,防止恶意篡改。

✅ 构建自动化CI流程

用GitHub Actions或Jenkins实现每日构建,每次提交自动生成新镜像并测试启动。


写在最后:掌握底层,才能真正自由

很多人觉得“我会用Docker、会配Yocto”就够了,但当你遇到一个板子死活启动不了、串口黑屏、内核卡在某个阶段的时候,你会发现——真正救你的,是你亲手走过一遍的这条路

构建最小化ARM64系统,不是为了炫技,而是为了建立一种“全栈掌控感”:你知道每一段代码何时被执行,每一个寄存器何时被设置,每一次跳转意味着什么。

当你能在16MB里放下一个完整的Linux系统,并且知道它为什么能运行,你就不再是工具的使用者,而是系统的缔造者。


如果你正在做智能网关、工控终端、无人机飞控或车载设备,这种能力会让你的设计更加高效、可靠、安全。而这一切,都始于一个最简单的/init脚本和一句“Hello”。

📢动手试试吧!评论区告诉我你第一次看到“Welcome to Minimal ARM64 Linux”出现在串口终端时的心情。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/3 6:37:48

Ryujinx模拟器实战指南:揭秘Switch游戏PC运行的黄金法则

Ryujinx模拟器实战指南&#xff1a;揭秘Switch游戏PC运行的黄金法则 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx 还在为Switch游戏无法在PC上畅玩而烦恼吗&#xff1f;这款基于C#开…

作者头像 李华
网站建设 2026/4/18 5:37:28

GHelper终极指南:让你的华硕笔记本重获新生的完整教程

GHelper终极指南&#xff1a;让你的华硕笔记本重获新生的完整教程 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and other models 项目地址…

作者头像 李华
网站建设 2026/4/18 5:33:26

OpCore Simplify:智能化黑苹果EFI配置终极解决方案

OpCore Simplify&#xff1a;智能化黑苹果EFI配置终极解决方案 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为繁琐的OpenCore配置步骤而头痛吗…

作者头像 李华
网站建设 2026/4/18 5:44:21

5分钟部署AI读脸术:OpenCV镜像实现零配置年龄性别识别

5分钟部署AI读脸术&#xff1a;OpenCV镜像实现零配置年龄性别识别 1. 项目背景与技术价值 在智能安防、人机交互、个性化推荐等场景中&#xff0c;人脸属性分析正成为一项关键的前置能力。其中&#xff0c;性别与年龄识别因其非侵入性、高实用性&#xff0c;被广泛应用于零售…

作者头像 李华
网站建设 2026/4/18 5:38:54

Holistic Tracking视频分析应用:运动轨迹提取步骤详解

Holistic Tracking视频分析应用&#xff1a;运动轨迹提取步骤详解 1. 技术背景与核心价值 在计算机视觉领域&#xff0c;人体动作分析一直是极具挑战性的研究方向。传统方法往往将面部、手势和身体姿态作为独立任务处理&#xff0c;导致系统复杂、数据割裂、实时性差。随着深…

作者头像 李华