做嵌入式 Linux 开发最烦人的事是什么?
烧 SD 卡。插板子。等启动。看串口。挂了。拔电源。重烧。
上一篇文章写了 busybox,已经有了最小的根文件系统,那我们是不是就可以构建自己的嵌入式 Linux 系统了?
所以这篇文章就来了,用 QUME 搭建自己的嵌入式 Linux 系统,让我们对 Linux 更加贴切的认识。。。。。

用 QEMU 模拟的话:
改一行代码 → make → 启动虚拟机 → 看 dmesg,30 秒。5 分钟够你试 10 次。
这不是「替代真机测试」——QEMU 没有 SPI Flash,没有 I2C 传感器,没有 DMA。它解决的是真机开发中那 80% 的「能不能编译过」「能不能加载」「会不会 panic」的快速验证。剩下 20% 硬件相关的 bug,你还是要买板子来测试实验。
精髓是——QEMU 让你在办公室就把内核玩烂了再上真机。
整个环境不到 50MB。一个 Linux 内核,一个 Busybox 根文件系统,一个 QEMU 虚拟机。从零到能跑,15 分钟。
三个零件——这是 Linux 初学者到进阶的核心,这也是理解 Linux 的核心,我们所有谈论的 Linux 就不应该只谈论 Linux 内核,因为它只是 Linux 系统的一部分。

内核(大脑):
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.40.tar.xztar -xf linux-6.12.40.tar.xz && cd linux-6.12.40make x86_64_defconfigmake -j$(nproc)输出 `arch/x86/boot/bzImage`,压缩后约 12MB,QEMU 直接用。
`x86_64_defconfig` 这个配置值得说一句——它是内核自带的「能跑就行」配置,包含了 Virtio 驱动(QEMU 的虚拟磁盘/网络)、ext4 文件系统、PCI 总线支持,刚好够在 QEMU 里启动到 shell。你要做 ARM 开发,换 `ARCH=arm64 make defconfig`,一样流程。做 RISC-V 也一样。
Busybox(工具集):
嵌入式 Linux 的瑞士军刀——一个不到 2MB 的二进制,提供了 ls、cp、mount、ifconfig、vi、wget 等 300 多个常用命令。
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2tar -xf busybox-1.36.1.tar.bz2 && cd busybox-1.36.1make defconfigsed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .configmake -j$(nproc)make install静态编译这里有一个坑,我当年卡了一整个下午。
如果你的 Busybox 是动态链接的,在 initramfs 里启动后 `/bin/sh` 会提示 `not found`——不是 sh 不存在,是动态链接器 `ld-linux.so` 不在 initramfs 里。错误信息极其误导人,你盯着文件看了半天,文件明明在,权限也对,它就是起不来。
所以一定记得 `CONFIG_STATIC=y`,或者把动态库手动拷进 initramfs。
initramfs(根文件系统):
内核启动后第一个挂载的文件系统。目录结构长这样:
/tmp/initramfs/├── bin/│ ├── busybox│ └── sh -> busybox├── sbin/│ └── init├── etc/├── proc/├── sys/└── dev/最关键的 `init` 脚本:
#!/bin/shmount -t proc proc /procmount -t sysfs sysfs /sysmount -t devtmpfs devtmpfs /devecho "=== Embedded Linux Ready ==="echo "Kernel: $(uname -r)"echo "Free memory: $(free -m | awk 'NR==2{print $4}') MB"exec /bin/sh打包:
cd /tmp/initramfsfind . -print0 | cpio --null -ov --format=newc | gzip -9 > ~/initramfs.cpio.gz最终产物 `initramfs.cpio.gz`,大约 1-2MB。
东西齐了,一行命令启动:
qemu-system-x86_64 \ -kernel ~/linux-6.12.40/arch/x86/boot/bzImage \ -initrd ~/initramfs.cpio.gz \ -append "console=ttyS0 nokaslr quiet" \ -nographic \ -m 256M \ -smp 2`-kernel` 直接加载内核,跳过 BIOS/UEFI 引导,省 2-3 秒。
`-initrd` 把 initramfs 加载到内存。
`-append "console=ttyS0"` 让内核日志输出到串口,`-nographic` 模式下显示在你的终端里。`nokaslr` 关掉内核地址随机化——调试时需要固定地址,不然断点打不上。`-m 256M` 分 256MB 内存,内核对 Busybox 绰绰有余。`-smp 2` 给两个 CPU 核心,你能在上头测试 SMP 内核模块。
3 秒开始滚屏。然后:

=== Embedded Linux Ready ===Kernel: 6.12.40Free memory: 232 MB/ #到这里,你有了一个完整的、可调试的 Linux 环境。写内核模块、改调度器、测试 eBPF 程序,全在这个沙箱里。
真正有价值的是 GDB 直接连 QEMU。
真机调试内核得用 JTAG 调试器,断点经常不稳,一套下来几千块。QEMU 内置 GDB server,零成本:
qemu-system-x86_64 -kernel bzImage -initrd initramfs.cpio.gz \ -append "console=ttyS0 nokaslr" -nographic -m 256M -s -S另一个终端:
$ gdb vmlinux(gdb) target remote :1234(gdb) break start_kernel(gdb) continue你可以在内核启动过程中下断点,单步跟踪你自己的内核模块,看内存和寄存器状态,测试 oops/panic 时的调用栈回溯。用这套东西学内核源码,效率是「读代码然后猜行为」的十倍以上。
写一个 hello world 内核模块,真机和 QEMU 的差距有多大?
真机流程:写代码 → 交叉编译 → 复制到 SD 卡(或 NFS)→ 插卡上电 → 等启动 → insmod → panic,板子挂了 → 拔电源 → 从头来。
QEMU 流程:写代码 → make → insmod → panic → Ctrl+C → 修复 → 重启,2 秒。
这是一种根本性的心理变化。
在真机上你不敢写冒险的代码——挂一次成本太高,重新烧卡接串口打开终端,心态已经崩了。在 QEMU 里挂就挂了,代码还在编辑器里,改完 20 秒后又是全新内核。
make -C ~/linux-6.12.40 M=$PWD modulesinsmod my_driver.kodmesg | tail -5这种「你敢写、敢改、敢试」的节奏,是内核开发学习曲线最陡那段最好的缓冲。
换 ARM 或者 RISC-V,改两行:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfigmake ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)qemu-system-aarch64 -M virt -cpu cortex-a53 -kernel Image -initrd initramfs.cpio.gz \ -append "console=ttyAMA0" -nographic -m 256Mmake ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfigqemu-system-riscv64 -M virt -kernel Image -initrd initramfs.cpio.gz \ -append "console=ttyS0" -nographic -m 256M`-M virt` 是 QEMU 的虚拟平台,没有真实硬件对应,但包含了 ARM/RISC-V 架构的通用外设(PCIe、virtio-blk、PL011 串口)。适合软件验证,不适合硬件驱动开发。
说实话,QEMU 不是万能的。
硬件外设不存在。QEMU 不会模拟你的 SPI Flash、I2C 传感器、CAN 控制器。如果你的内核模块是操作具体硬件的,最终还是要上真机。这不是 QEMU 的缺陷,是虚拟化本身的边界。
时序不可信。QEMU 的指令执行时间是模拟的,中断延迟、DMA 传输时间跟真实硬件没有可比性。依赖精确定时的代码——比如用 `ndelay()` 做时序控制——在 QEMU 能跑不代表真机能跑。
内存模型简化。QEMU 默认平坦内存模型。如果你的代码涉及 cache coherency、内存屏障、DMA 缓存一致性——这些在 QEMU 里都不会暴露。必须上真机。
Buildroot 是更完整的替代方案。如果你需要的不只是内核加 shell,还要 Qt、OpenSSL、Python、蓝牙协议栈,手动编译不是好主意。
git clone git://git.busybox.net/buildrootcd buildrootmake qemu_x86_64_defconfigmake -j$(nproc)./output/images/start-qemu.shBuildroot 帮你管了工具链、库依赖、文件系统打包。代价是 build 一次 30 分钟,失去手写 init 脚本的透明度。两个都得会——开发实验阶段用手工环境,验证完整功能切到 Buildroot。
嵌入式 Linux 开发环境搭建,说到底就是「快速失败」的问题。
你把「改一行代码到看到结果」从 5 分钟压到 30 秒,就能多试 10 种方案。多试的 10 次里,至少有一次能让你少烧一张 SD 卡。
今天的脚本,明天就能出第一个内核模块。

END
来源:嵌入式Linux