湖南省网站建设_网站建设公司_悬停效果_seo优化
2026/1/14 5:34:53 网站建设 项目流程

从零构建一个能跑起来的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”出现在串口终端时的心情。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询