没有开发板也能学嵌入式 Linux:Ubuntu 20.04 + QEMU + Buildroot 跑通 ARM64
想学嵌入式 Linux,不一定要先买开发板。使用 QEMU 的
virt虚拟平台和 Buildroot,可以在 x86_64 Ubuntu 主机上构建并启动一套 ARM64 Linux 系统。验证链路:Ubuntu x86_64 主机 → QEMU 全系统模拟 → Buildroot 构建 Linux 和 rootfs → 登录 ARM64 shell → 交叉编译并运行测试程序。
本文没有使用实体开发板。文中的运行验证仅针对 QEMU 模拟环境,不代表真实板卡的 U-Boot、设备树、驱动或外设已经验证。
⚠️ Ubuntu 20.04 LTS 的标准维护已于 2025 年结束。本文以现有环境兼容为目的;继续联网使用时,请确认软件源和安全维护方案。
📅 本文实验记录于 2026 年 10 月,文中命令输出中的时间戳与版本号为当时实际运行结果,请以你所选 Buildroot 版本的输出为准。新项目建议评估仍处于标准维护期的 Ubuntu 版本。
一、目标效果
完成后,QEMU 终端中应能看到类似登录提示:
Welcome to Buildroot buildroot login: root # uname -m aarch64Buildroot 默认通常允许 root 登录且不设置密码;如果提示输入密码,可直接回车。实际提示以所选 Buildroot 配置为准。
图注:QEMU 中的 Buildroot Linux 启动并登录成功,系统架构为 AArch64。
画面中的Welcome to Buildroot表示系统已进入登录阶段,使用root登录后出现的#是 root shell 提示符。执行uname -m得到aarch64,说明当前操作的是 QEMU 中的 ARM64 系统,而不是外层 x86_64 Ubuntu 主机。此结果验证了模拟系统的启动与登录,测试程序的交叉编译和运行将在后续步骤验证。
二、实验环境
| 项目 | 要求或示例 |
|---|---|
| 宿主机 | Ubuntu 20.04 x86_64 |
| 系统 QEMU | 4.2.1,Ubuntu 软件包安装版本 |
| 权限 | 可使用sudo安装依赖 |
| 内存 | 建议至少 4 GB |
| 磁盘 | 构建目录建议预留至少 30 GB 可用空间;使用虚拟机时,虚拟硬盘建议 60 GB 起步 |
| 网络 | 首次构建需要下载 Buildroot 及其源码包 |
确认宿主机架构:
uname-m输出:
x86_64三、安装依赖
更新软件包索引并安装 QEMU 与 Buildroot 构建依赖:
sudoaptupdatesudoaptinstall-y\qemu-system-arm\build-essentialmakegcc g++\bcbison flex\libncurses-dev libssl-dev libelf-dev\cpiounziprsyncfilewgetcurlgit\python3Ubuntu 的qemu-system-arm软件包提供 ARM/AArch64 system emulator。检查 QEMU:
qemu-system-aarch64--versionBuildroot 会为所选目标构建交叉编译工具链,因此后续编译测试程序时优先使用 Buildroot 生成的编译器。这样可以让程序的 C 库和 ABI 与 Buildroot rootfs 保持一致。
embed@embedtest:~$ qemu-system-aarch64 --version QEMU emulator version 4.2.1 (Debian 1:4.2-3ubuntu6.30)Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers上述输出记录的是 Ubuntu 系统安装的 QEMU 4.2.1,本文手动启动命令明确使用/usr/bin/qemu-system-aarch64。Buildroot 也可能构建自己的主机侧 QEMU;该程序与系统 QEMU 是两份独立的安装,不能仅凭系统版本输出确定启动脚本实际使用的版本。
四、下载并解压 Buildroot
本文固定使用 Buildroot2025.02.8作为示例,便于复现;其他版本的配置菜单和内核版本可能不同。也可从 Buildroot 官方下载页 选择仍受支持的稳定版,并相应替换文件名。
cd"$HOME"wgethttps://buildroot.org/downloads/buildroot-2025.02.8.tar.gztarxf buildroot-2025.02.8.tar.gzcdbuildroot-2025.02.8本文实际源码目录为~/buildroot-2025.02.8,后续相对路径命令均在此目录执行。
Buildroot 2025.02.8 解压后的源码目录。可以看到 configs、package、toolchain 和 Makefile 等内容,后续配置与编译均在此目录执行。
embed@embedtest:~/buildroot-2025.02.8$ ls arch board boot CHANGES Config.in Config.in.legacy configs COPYING DEVELOPERS docs fs linux Makefile Makefile.legacy package README support system toolchain utils五、选择 QEMU ARM64 virt 配置
Buildroot 为 QEMU 通用virt平台提供了预设配置:
makeqemu_aarch64_virt_defconfig该配置会为 ARM64 QEMU virt 平台选择内核、rootfs 和相应启动设置。Buildroot 不同版本的默认文件系统格式或生成文件可能略有差异;后续命令以实际output/images/内容为准。
省略配置工具的构建输出,关键结果如下:
embed@embedtest:~/buildroot-2025.02.8$ make qemu_aarch64_virt_defconfig # # configuration written to /home/embed/buildroot-2025.02.8/.config #上述提示说明预设配置已成功写入.config,并不代表内核和根文件系统已经编译完成。接下来再执行系统编译。
六、编译系统
首次构建会下载并编译大量源码包,耗时取决于网络、CPU 和磁盘性能:
make-j"$(nproc)"如果内存不足或系统开始大量使用 swap,降低并行度,例如:
make-j2成功后查看生成物:
ls-lhoutput/images/本次构建生成的文件如下:
embed@embedtest:~/buildroot-2025.02.8$ ls -lh output/images/ total 21M -rw-r--r-- 1 embed embed 12M Oct 12 06:25 Image -rw-r--r-- 1 embed embed 60M Oct 12 06:25 rootfs.ext2 lrwxrwxrwx 1 embed embed 11 Oct 12 06:25 rootfs.ext4 -> rootfs.ext2 -rwxr-xr-x 1 embed embed 832 Oct 12 06:25 start-qemu.sh这些文件各有用途:
| 文件 | 作用 |
|---|---|
Image | ARM64 Linux 内核,启动后负责管理 QEMU 模拟的硬件 |
rootfs.ext2 | 根文件系统镜像,包含 shell、基本命令、运行库和系统配置 |
rootfs.ext4 | 指向rootfs.ext2的符号链接,与它使用同一份镜像,不是另一份磁盘文件,无需手动复制 |
start-qemu.sh | Buildroot 生成的启动脚本,负责将内核和根文件系统交给 QEMU 启动 |
rootfs.ext2的文件名并不能单独确定内部文件系统格式,可用file output/images/rootfs.ext2检查。这里的rootfs.ext4链接是 Buildroot 生成的,不是通过改名转换文件系统。
输出中的total 21M表示这些文件实际占用的磁盘块总量,而60M是 rootfs 镜像的逻辑大小。稀疏镜像中未分配的区域不占用相应磁盘块,因此两者可能不同。
看到这些文件说明关键构建产物已生成;还需确认make正常结束、没有错误,并完成下一节的启动和登录验证,才能确认系统可运行。
七、启动 ARM64 Linux
方式一:使用 Buildroot 启动脚本
先查看脚本中调用的 QEMU 路径。如果使用output/host/bin/下的程序,应另行记录其版本,不要将前面的系统 QEMU 4.2.1 作为脚本运行版本:
catoutput/images/start-qemu.sh确认后启动:
./output/images/start-qemu.sh脚本会使用与该 defconfig 对应的 QEMU 参数和镜像。等待内核启动完成,在提示符处输入root登录;如出现密码提示,按当前 Buildroot 配置输入,默认无密码时直接回车。出现#提示符即表示已进入 Buildroot shell,启动和登录效果见文章开头的“目标效果”部分。
方式二:手动启动 QEMU
如果输出目录里有Image和rootfs.ext2,可以参考下面的命令手动启动:
/usr/bin/qemu-system-aarch64\-Mvirt\-cpucortex-a53\-smp2\-m1G\-nographic\-kerneloutput/images/Image\-append"rootwait root=/dev/vda console=ttyAMA0"\-drivefile=output/images/rootfs.ext2,if=none,format=raw,id=hd0\-devicevirtio-blk-device,drive=hd0此命令假定内核和 rootfs 的文件名、块设备驱动及串口配置与当前 Buildroot 配置一致。如果手动启动失败,先使用 Buildroot 生成的start-qemu.sh;脚本通常包含该预设所需的完整参数。不要将示例文件名或参数当作所有版本的固定接口。
手动命令启动成功后,登录界面与文章开头的“目标效果”一致,此处不再重复放图。请实际执行手动命令,确认能够登录并运行uname -m,输出应为aarch64;启动脚本运行成功不等于手动命令也已验证通过。
八、理解常见 QEMU 参数
| 参数 | 含义 |
|---|---|
-M virt | 使用 QEMU ARM 通用虚拟平台,不对应某一块真实开发板 |
-cpu cortex-a53 | 模拟 Cortex-A53 CPU 型号 |
-smp 2 | 提供 2 个虚拟 CPU |
-m 1G | 分配 1 GB 内存 |
-nographic | 不创建图形窗口,将串口控制台连接到当前终端 |
-kernel | 指定内核镜像 |
-append | 设置传给内核的启动参数 |
rootwait | 等待根设备就绪后再挂载 |
root=/dev/vda | 指定 virtio 块设备上的根文件系统 |
console=ttyAMA0 | 将控制台输出到 ARM PL011 串口 |
-drive | 定义虚拟磁盘镜像 |
-device virtio-blk-device | 将磁盘连接到 virtio 总线 |
九、在模拟系统中检查环境
登录 QEMU 中的 Buildroot 后执行:
uname-auname-muname -m应显示aarch64。内核版本取决于 Buildroot 配置,不要把示例中的版本号当作固定值。
本次运行输出如下:
# uname -a Linux buildroot 6.12.27 #1 SMP Mon Oct 12 06:24:57 UTC 2026 aarch64 GNU/Linux # uname -m aarch64检查 CPU、内存和根目录:
cat/proc/cpuinfofree-hls/QEMU 显示的是模拟硬件信息;这只能确认当前虚拟机配置,不代表真实开发板的 CPU、内存或外设情况。
# cat /proc/cpuinfo processor : 0 BogoMIPS : 125.00 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid CPU implementer : 0x41 CPU architecture: 8 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 processor : 1 BogoMIPS : 125.00 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid CPU implementer : 0x41 CPU architecture: 8 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 # free -h total used free shared buff/cache available Mem: 990.9M 16.6M 970.6M 44.0K 3.7M 939.4M Swap: 0 0 0 # ls / bin lib media root tmp crond.reboot lib64 mnt run usr dev linuxrc opt sbin var etc lost+found proc sys十、交叉编译并运行测试程序
1. 在宿主机编写程序
退出或暂时挂起 QEMU 会话后,在 Buildroot 源码目录所在的宿主机终端创建测试程序:
cd"$HOME/buildroot-2025.02.8"cat>hello.c<<'EOF' #include <stdio.h> int main(void) { puts("Hello from ARM64 Linux on QEMU!"); return 0; } EOF2. 使用 Buildroot 生成的交叉编译器
Buildroot 交叉编译器位于output/host/bin/。先查找实际编译器名称:
findoutput/host/bin-maxdepth1-name'aarch64-buildroot-linux-*-gcc'-print自动找到 Buildroot 生成的交叉编译器并编译。使用 Buildroot 自己的工具链能避免 Ubuntu 系统交叉工具链与 rootfs 的 libc 或 ABI 不匹配:
CROSS_GCC=$(findoutput/host/bin-maxdepth1-name'aarch64-buildroot-linux-*-gcc'-print-quit)test-n"$CROSS_GCC"||{echo"Buildroot AArch64 compiler not found";exit1;}"$CROSS_GCC"hello.c-ohellofilehellofile应显示 64 位 ARM AArch64 ELF。不要直接用 Ubuntu 的aarch64-linux-gnu-gcc编译后就假定它能在 Buildroot rootfs 运行;不同工具链的动态加载器和 C 库可能不兼容。
embed@embedtest:~/buildroot-2025.02.8$ file hello hello: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 6.12.0, not stripped输出中的ARM aarch64确认了目标架构,动态加载器为/lib/ld-linux-aarch64.so.1。shared object可能是当前file版本对 PIE(位置无关可执行文件)的识别结果,不一定表示生成了共享库;能否正常运行仍需在 QEMU 中验证。
3. 将程序放入 rootfs 镜像
挂载并修改镜像前,必须先关闭使用同一 rootfs 镜像的 QEMU。可在 Buildroot 中执行poweroff,若 QEMU 未自动退出,再按Ctrl+A,松开后按X。仅暂停 QEMU 不够;不要让 QEMU 和宿主机同时写入镜像,以免损坏文件系统。
先确认 rootfs 镜像格式:
ls-lhoutput/images/rootfs.*fileoutput/images/rootfs.*若生成的是rootfs.ext2,可在宿主机临时挂载镜像并复制程序:
sudomkdir-p/mnt/buildroot-rootfssudomount-oloop,rw output/images/rootfs.ext2 /mnt/buildroot-rootfssudoinstall-m0755 hello /mnt/buildroot-rootfs/root/hellosudoumount/mnt/buildroot-rootfs这里只修改生成的 rootfs 镜像,不要把文件复制到output/target后就认为已有镜像会自动更新。若使用其他文件系统格式,请按相应工具和格式处理。
4. 在 QEMU 中运行
重新启动 QEMU:
./output/images/start-qemu.sh登录后运行:
/root/hello本次运行输出:
Hello from ARM64 Linux on QEMU!这验证了 Buildroot 工具链、该 Buildroot rootfs 和当前 QEMU 虚拟平台组成的交叉编译运行闭环;仍不等于在实体开发板上验证通过。
图注:交叉编译的 hello 程序在 QEMU ARM64 Linux 中运行成功。
十一、退出 QEMU
优先在 QEMU 的 Buildroot shell 中执行正常关机,让系统完成文件写入并卸载文件系统:
poweroff若关机完成后 QEMU 没有自动退出,使用-nographic启动时可按Ctrl+A,松开后按X,返回 Ubuntu 终端。该快捷键直接退出 QEMU,不应作为正常关机的替代,以免正在写入的文件丢失或损坏。
如果快捷键被终端拦截,先查看当前 QEMU 的控制台设置或使用start-qemu.sh对应的退出方式;不要直接关闭宿主机。
十二、常见问题
1. qemu-system-aarch64: command not found
确认安装了 Ubuntu 的 ARM system emulator 包:
sudoaptinstallqemu-system-arm2. 启动后停在 Booting the kernel.
先用 Buildroot 生成的start-qemu.sh启动,并确认内核串口控制台配置与 QEMU 的console=ttyAMA0一致。手动命令中的内核文件、机器类型或启动参数与当前配置不匹配,也可能导致没有控制台输出。
3. Kernel panic,无法挂载 rootfs
检查以下内容是否彼此匹配:
- QEMU 的磁盘镜像路径和实际文件名
- 内核是否包含 virtio 块设备及所用文件系统驱动
root=/dev/vda是否与磁盘设备类型相符- Buildroot 生成的启动脚本使用了哪些实际参数
4. 交叉编译程序启动失败
常见原因是使用的宿主机交叉编译器与 Buildroot rootfs 使用了不同 libc、动态加载器或 ABI。优先使用output/host/bin/下 Buildroot 生成的交叉编译器重新编译。
5. 运行 /root/hello 提示 not found
本次曾出现如下错误:
# /root/hello -sh: /root/hello: not found先在 QEMU 的 Buildroot shell 中检查程序和它请求的动态加载器:
ls-l/root/hellols-l/lib/ld-linux-aarch64.so.1本次检查结果是/root/hello不存在,而动态加载器存在。因此直接的问题是程序没有出现在当前启动的 rootfs 中,并非已证明交叉编译器或运行库不兼容。
关闭 QEMU 后,回到 Ubuntu 的 Buildroot 源码目录,按第十节重新挂载实际使用的 rootfs 镜像;确认挂载成功,再复制hello,用sudo ls -l /mnt/buildroot-rootfs/root/hello检查文件是否存在。卸载成功后,使用同一份镜像重新启动 QEMU。不要在 QEMU 运行时修改镜像,也不要在手动复制后立即运行make,以免重新生成 rootfs 覆盖文件。
如果程序存在但动态加载器缺失,shell 也可能报告not found。此时应根据程序实际请求的加载器路径检查工具链与 rootfs 是否匹配;不要随意复制宿主机的 x86_64 库或关闭校验来解决。
6. Buildroot 报 Python 版本或依赖错误
核对所用 Buildroot 版本的宿主机依赖说明,并安装缺少的依赖。Ubuntu 20.04 的系统 Python 版本通常可满足本文固定的 Buildroot 版本;若实际检查不满足要求,应使用独立安装的 Python 环境或选择兼容的 Buildroot 版本,不要替换系统 Python,以免影响系统工具。
十三、这次实验验证了什么
| 项目 | 本文验证范围 |
|---|---|
| QEMU ARM64 全系统模拟 | 在 x86_64 Ubuntu 上启动 QEMU virt |
| Buildroot | 构建 ARM64 Linux 内核和 rootfs |
| 交叉编译 | 使用 Buildroot 工具链生成 ARM64 程序 |
| 运行程序 | 在对应 Buildroot rootfs 的 QEMU 系统中执行程序 |
| 实体板启动 | 未验证;需要真实开发板、对应 U-Boot、设备树、内核配置及硬件测试 |
十四、总结与后续
没有开发板也可以先熟悉交叉编译、Linux 启动和 rootfs 的基本关系。QEMUvirt是通用模拟平台,不是具体产品板卡;模拟器中运行正常,不能证明某块板子的 U-Boot、设备树、驱动、网络 PHY 或外设都能工作。
程序已经跑起来了,但如果结果不对,该怎么知道它在哪一步出了问题?没有开发板,也能继续练习嵌入式开发中常用的断点、单步和变量检查。
下一篇预告:《没有开发板也能调试 ARM64 程序:QEMU + GDB 远程调试实战》
下一篇计划为 Buildroot 添加
gdbserver,建立 Ubuntu 与 QEMU 中 ARM64 Linux 的调试连接,再用一个小程序练习设置断点、单步执行、查看变量和调用栈。从“程序能运行”到“知道程序为什么这样运行”,我们继续在 QEMU 中把这个过程走一遍。关注这个系列,后续还会实践 QEMU 网络配置和 Buildroot rootfs 定制,不用等开发板到手再开始。