一、实验概述本实验围绕 Linux 内核移植、最小系统启动与内核裁剪展开目标是让读者完整经历内核被移植到目标板 → 没有 RootFS 时为何不能形成可用系统 → 加入最小 RootFS 后进入 shell → 裁剪内核后仍能进入同一个 shell的完整过程。实验环境分为 HOST 与 TARGET 两个窗口窗口环境关键组件A 窗口 / HOSTUbuntu 18.04 LTS 虚拟机用户 easyboard交叉工具链 Arm GNU Toolchain 12.3.Rel1arm-none-linux-gnueabihf-B 窗口 / TARGETWindows QEMU 11.1.0vexpress-a9 / Cortex-A9Bootloader U-Boot 2026.07实验一已验证 TFTP 可用共享目录为 WindowsE:\EmbeddedLinuxLab↔ Ubuntu/mnt/EmbeddedLinuxLab。本实验内核采用 Linux 6.6 系列草稿基线实测后固定 6.6.y 小版本最小 RootFS 采用 BusyBox initramfs仅用于形成可交互的最小系统。二、四阶段实验路线本实验不把“内核启动到 panic”作为终点而是让学生完整看到四个阶段阶段关键动作学生应获得的认识阶段 A内核移植Linux 6.6 → multi_v7_defconfig → VExpress/PL011 → zImage DTB让通用 ARMv7 内核适配 VExpress-A9阶段 B无 RootFS 对照U-Boot TFTP → bootz → Linux 启动 → VFS/Kernel panic理解 Kernel ≠ 完整 Linux 系统阶段 C加入最小 RootFSBusyBox → initramfs → U-Boot 同时加载 Kernel/DTB/RootFS →/ #第一次真正“进入系统”阶段 D内核裁剪menuconfig 关闭 CONFIG_NET → 重编 zImage → 用同一 RootFS 再启动观察“按需求删除功能但系统核心仍可工作”本实验的三个核心问题Linux 内核怎样变成能在 VExpress-A9 上运行的内核为什么只有 Kernel 还不能形成一个可交互的 Linux 系统关闭整个网络子系统后系统还能否启动到 shell镜像大小和配置发生什么变化三、实验目标理解“Linux 内核移植”的含义不是重写内核而是在已有 ARMv7 支持基础上为目标板选择体系结构配置、设备树和启动参数。掌握内核基本构建链源码 → Kconfig/.config → 交叉编译 → zImage / DTB。通过 U-Boot 的 TFTP 与 bootz 命令加载并启动 Linux 内核。通过“无 RootFS 启动失败”这个中间现象理解 Kernel 与用户空间的边界。制作一个最小 BusyBox initramfs使系统最终进入/ #shell并能执行 uname、ls、mount、cat /proc/cpuinfo 等命令。完成一次真实裁剪关闭 CONFIG_NET比较裁剪前后的 .config、zImage 大小和启动行为。理解 U-Boot 网络和 Linux Kernel 网络属于两个独立软件层次即使裁掉 Linux 网络U-Boot 仍可通过 TFTP 下载裁剪版内核。四、实验目录与准备4.1 HOST 端目录mkdir-p~/EmbeddedLinuxLab/lab02mkdir-p~/EmbeddedLinuxLab/build/lab02cd~/EmbeddedLinuxLab/lab02源码和编译过程继续放在 Ubuntu 原生文件系统中/mnt/EmbeddedLinuxLab只作为 HOST ↔ TARGET 的交换区不直接在 VMware 共享目录中编译 Kernel 或 BusyBox。4.2 TARGET 端目录E:\EmbeddedLinuxLab\ ├─ images\ ├─ scripts\ ├─ logs\ └─ tftp\Ubuntu 中对应固定挂载点/mnt/EmbeddedLinuxLab4.3 检查 ARM32 交叉编译环境source~/EmbeddedLinuxLab/env-arm32.sh arm-none-linux-gnueabihf-gcc--version|head-n1echo$ARCHecho$CROSS_COMPILE预期GCC 12.3.1 左右ARCHarmCROSS_COMPILEarm-none-linux-gnueabihf-。4.4 安装构建依赖Ubuntu 18.04sudoaptupdatesudoaptinstall-ybuild-essentialgitwgetxz-utilsbcbison flex\libssl-dev libelf-dev libncurses5-dev device-tree-compiler cpio提示如果某个包已经安装apt 会直接跳过如 Ubuntu 18.04 软件源出现失效或 404请记录完整报错不要把新版 Ubuntu 的 deb 包直接混入 18.04。五、第一阶段获取 Linux 6.6 源码5.1 方案 AUbuntu 直接下载cd~/EmbeddedLinuxLab/srcwgethttps://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xztar-xJflinux-6.6.tar.xzcdlinux-6.65.2 方案 BWindows 浏览器下载网络较慢时推荐如果 Ubuntu 虚拟机下载较慢可在 Windows 浏览器下载 linux-6.6.tar.xz暂存到E:\EmbeddedLinuxLab\tftp或另建 downloads再从 Ubuntu 复制到本地源码区。cp/mnt/EmbeddedLinuxLab/tftp/linux-6.6.tar.xz ~/EmbeddedLinuxLab/src/cd~/EmbeddedLinuxLab/srctar-xJflinux-6.6.tar.xzcdlinux-6.6版本说明草稿先固定 Linux 6.6 系列目的是减少版本变量。最终教学母盘可在实测稳定后固定一个具体 6.6.y 小版本。六、第二阶段生成 ARMv7 / VExpress 基线配置source~/EmbeddedLinuxLab/env-arm32.shcd~/EmbeddedLinuxLab/src/linux-6.6makemrpropermakemulti_v7_defconfig这里故意采用multi_v7_defconfig它代表一份较通用的 ARMv7 内核配置便于后面通过裁剪把“通用内核”逐步变成更接近产品需求的专用内核。6.1 验证 VExpress、串口和 initramfs 支持grepCONFIG_ARCH_VEXPRESS .configgrepCONFIG_SERIAL_AMBA_PL011 .configgrepCONFIG_SERIAL_AMBA_PL011_CONSOLE .configgrepCONFIG_BLK_DEV_INITRD .configgrepCONFIG_RD_GZIP .configgrepCONFIG_DEVTMPFS .config预期至少应看到 VExpress、PL011 串口控制台和 initramfs/initrd 支持被启用。若某一项与预期不符请保留输出后续按实测修订配置步骤。教学点.config是内核“包含哪些功能”的核心配置文件menuconfig只是修改.config的一种菜单式界面。6.2 保存“完整基线配置”mkdir-p~/EmbeddedLinuxLab/build/lab02cp.config ~/EmbeddedLinuxLab/build/lab02/config_full七、第三阶段编译 zImage 与设备树make-j$(nproc)zImage dtbs第一次完整编译请记录时间后续裁剪版增量编译也可记录用于比较。实测完整内核第一次编译时间约两小时。这里需要注意我之前将虚拟机的设置成 4 核 2GB内存太小了后调至 8G 有改善但中间由于 Ubuntu 的屏保和自动挂起设置中断几次再次恢复时请注意使用make -j4 zImage dtbs这样凡是完整、有效的就直接复用。7.1 检查 zImagels-lharch/arm/boot/zImagefilearch/arm/boot/zImage7.2 定位 VExpress-A9 的 DTBfindarch/arm/boot/dts-namevexpress-v2p-ca9.dtb-printLinux 6.6 的 ARM DTS 目录可能按厂商重新分层因此不把 DTB 路径写死以find的实际结果为准。实测本机实际 DTB 路径~/EmbeddedLinuxLab/src/linux-6.6/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb7.3 保存并部署完整内核cparch/arm/boot/zImage ~/EmbeddedLinuxLab/build/lab02/zImage_fullcparch/arm/boot/zImage /mnt/EmbeddedLinuxLab/tftp/zImage_fullcp实际DTB路径/vexpress-v2p-ca9.dtb /mnt/EmbeddedLinuxLab/tftp/cp./arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb /mnt/EmbeddedLinuxLab/tftp/7.4 记录完整内核大小stat-c%n : %s bytes~/EmbeddedLinuxLab/build/lab02/zImage_fullls-lh~/EmbeddedLinuxLab/build/lab02/zImage_full实测zImage_full10,904,064 bytes约 11 MB八、第四阶段先不提供 RootFS观察“只有内核”的结果这一阶段不是实验终点而是一个非常短的对照实验故意只提供 zImage DTB让学生看到内核已经开始运行但没有根文件系统时不能形成完整可交互系统。8.1 Windows CMD 启动 QEMU / U-BootC:\Program Files\qemu\qemu-system-arm.exe ^ -machine vexpress-a9 ^ -cpu cortex-a9 ^ -m 512M ^ -nographic ^ -no-reboot ^ -kernel E:\EmbeddedLinuxLab\images\u-boot ^ -nic user,tftpE:\EmbeddedLinuxLab\tftp8.2 U-Boot 下载 zImage 与 DTB setenv kernel_addr_r 0x60100000 setenv fdt_addr_r 0x62000000 tftpboot ${kernel_addr_r} zImage_full tftpboot ${fdt_addr_r} vexpress-v2p-ca9.dtb实测zImage_full10904064 bytes速度 2.5 MiB/sDTB14129 bytes速度 104.5 KiB/s8.3 设置命令行并启动无 RootFS setenv bootargs consolettyAMA0,38400n8 bootz ${kernel_addr_r} - ${fdt_addr_r}其中bootz的三个位置参数依次为zImage 地址、initrd/initramfs 地址此处没有用-、设备树地址。8.4 预期观察Starting kernel ... Booting Linux on physical CPU 0x0 Linux version 6.6... CPU: ARMv7 Processor ... Machine model: V2P-CA9 ...随后由于没有 RootFS通常会在 VFS 阶段停止并出现 Kernel panic具体文字以实测为准VFS: Unable to mount root fs ... Kernel panic - not syncing: VFS: Unable to mount root fs ...不要误判只要已经看到 Linux version、CPU、Machine model 等信息随后因 RootFS 缺失而 panic说明 “U-Boot → Kernel → DTB” 链路基本成功。接下来给它补上最小 RootFS。实测启动日志请记录本机最关键的 1530 行启动日志Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 6.6.0 (easyboardV3S) (arm-none-linux-gnueabihf-gcc (Arm GNU Toolchain 12.3.Rel1 (Build arm-12.35)) 12.3.1 20230626, GNU ld (Arm GNU Toolchain 12.3.Rel1 (Build arm-12.35)) 2.40.0.20230627) #2 SMP Sun Oct 4 21:38:32 CST 2026 [ 0.000000] CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr10c5387d [ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache [ 0.000000] OF: fdt: Machine model: V2P-CA9 [ 0.000000] OF: fdt: Ignoring memory block 0x80000000 - 0x80000004 …… [ 0.000000] percpu: Embedded 16 pages/cpu s36180 r8192 d21164 u65536 [ 0.000000] Kernel command line: consolettyAMA0,38400n8 …… [ 2.441269] CAN device driver interface [ 2.445504] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded [ 2.447912] e1000e: Intel(R) PRO/1000 Network Driver [ 2.447943] e1000e: Copyright(c) 1999 - 2015 Intel Corporation. [ 2.448118] igb: Intel(R) Gigabit Ethernet Network Driver [ 2.448144] igb: Copyright (c) 2007-2014 Intel Corporation. [ 2.619897] smsc911x 4e000000.ethernet eth0: MAC Address: 52:54:00:12:34:56 …… [ 5.194460] /dev/root: Cant open blockdev [ 5.199253] VFS: Cannot open root device or unknown-block(0,0): error -6 [ 5.202411] Please append a correct root boot option; here are the available partitions: [ 5.206873] 0100 65536 ram0 [ 5.206873] 0100 65536 ram0 [ 5.207411] (driver?) …… [ 5.617938] 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 5.622392] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---做到这一步发现自己的虚拟机总是莫名其妙的死机做了如下处理结束 vmware-vmx → 关闭 Hyper-V/WSL2/VBS → 重启 WindowsVMware 改 2 vCPU 8GB 关闭显示器 3D卸掉 spice-vdagent给 QEMU/KVM/SPICE 用的在 VMware 里没有实际用途九、第五阶段制作最小 BusyBox RootFSinitramfs这一部分只制作“足够进入 shell”的最小用户空间不把 RootFS 制作本身变成实验重点。后续可以再用 Buildroot 做更完整、更工程化的根文件系统实验。9.1 获取 BusyBox 源码cd~/EmbeddedLinuxLab/srcwgethttps://busybox.net/downloads/busybox-1.36.1.tar.bz2tar-xjfbusybox-1.36.1.tar.bz2cdbusybox-1.36.1如果 Ubuntu 下载较慢也可以先在 Windows 浏览器下载 busybox-1.36.1.tar.bz2放入E:\EmbeddedLinuxLab\tftp再复制到 Ubuntu 本地源码目录解压。9.2 配置 BusyBox 为静态 ARM 程序source~/EmbeddedLinuxLab/env-arm32.shmakedistcleanmakedefconfigmakemenuconfig在 BusyBox 菜单中找到Settings → Build static binary (no shared libs)将其选中。这样 BusyBox 不依赖目标板上的动态链接库特别适合最小 initramfs。grep^CONFIG_STATIC.config预期CONFIG_STATICy。9.3 编译并安装到 rootfs 目录make-j$(nproc)CROSS_COMPILEarm-none-linux-gnueabihf-rm-rf~/EmbeddedLinuxLab/build/lab02/rootfsmakeCROSS_COMPILEarm-none-linux-gnueabihf-\CONFIG_PREFIX~/EmbeddedLinuxLab/build/lab02/rootfsinstallfile~/EmbeddedLinuxLab/build/lab02/rootfs/bin/busybox应看到 ARM 32-bit 可执行文件并且为静态链接statically linked。9.4 建立最小目录结构在rootfs目录下创建最小化的目录骨架并放入必要的设备节点与初始化脚本cd~/EmbeddedLinuxLab/build/lab02/rootfsmkdir-pdev proc sys tmp etc/init.d说明dev、proc、sys是内核与用户空间交互的虚拟文件系统挂载点etc/init.d用于放置开机初始化脚本。9.5 创建 /initinitramfs 的入口是/init内核启动后会把它作为第一个用户进程执行。这里用 BusyBox 的sh作为 init并挂载必要的虚拟文件系统cat~/EmbeddedLinuxLab/build/lab02/rootfs/initEOF #!/bin/sh mount -t proc none /proc mount -t sysfs none /sys mount -t devtmpfs none /dev echo Welcome to BusyBox initramfs! exec /bin/sh EOFchmodx ~/EmbeddedLinuxLab/build/lab02/rootfs/init教学点/init是 initramfs 的固定入口内核在挂载 initramfs 后直接执行它而不是/sbin/init。9.6 创建最基本设备节点虽然devtmpfs会在运行时自动创建设备节点但为了保险起见仍手动创建控制台与 null 设备cd~/EmbeddedLinuxLab/build/lab02/rootfssudomknod-m622dev/console c51sudomknod-m666dev/null c13提示dev/console是内核控制台设备dev/null是空设备若后续启动时串口无输出优先检查这两项。9.7 打包为 initramfs使用cpio把rootfs目录打包成 initramfs 镜像并压缩为 gzip 格式cd~/EmbeddedLinuxLab/build/lab02/rootfsfind.|cpio-Hnewc-o|gzip~/EmbeddedLinuxLab/build/lab02/initramfs.gzls-lh~/EmbeddedLinuxLab/build/lab02/initramfs.gz说明cpio -H newc是内核 initramfs 的标准打包格式gzip压缩后内核可自动解压。9.8 检查并部署 RootFS把 initramfs 复制到 TFTP 目录供 U-Boot 下载cp~/EmbeddedLinuxLab/build/lab02/initramfs.gz /mnt/EmbeddedLinuxLab/tftp/initramfs.gzls-lh/mnt/EmbeddedLinuxLab/tftp/initramfs.gz实测initramfs.gz 约 1.2 MB含 BusyBox 静态二进制与最小目录结构。十、第六阶段用最小 RootFS 启动系统10.1 U-Boot 下载 Kernel / DTB / RootFSsetenv kernel_addr_r 0x60100000setenv fdt_addr_r 0x62000000setenv ramdisk_addr_r 0x63000000tftpboot${kernel_addr_r}zImage_fulltftpboot${fdt_addr_r}vexpress-v2p-ca9.dtbtftpboot${ramdisk_addr_r}initramfs.gz10.2 设置命令行并启动带 RootFSsetenv bootargsconsolettyAMA0,38400n8 root/dev/ram0 rwbootz${kernel_addr_r}${ramdisk_addr_r}${fdt_addr_r}说明root/dev/ram0告诉内核使用 initramfs 作为根文件系统rw允许读写。10.3 预期观察Starting kernel...... Freeing unused kernel memory:... Welcome to BusyBox initramfs!/#进入/ #后可执行以下命令验证系统可用/# uname -a/# ls //# mount/# cat /proc/cpuinfo实测成功进入/ #shelluname -a显示 Linux 6.6.0cat /proc/cpuinfo显示 ARMv7 Processor。十一、第七阶段裁剪内核关闭 CONFIG_NET11.1 备份完整配置并进入裁剪cd~/EmbeddedLinuxLab/src/linux-6.6cp.config ~/EmbeddedLinuxLab/build/lab02/config_full_backupmakemenuconfig在 menuconfig 中进入Networking support取消选中去掉*保存退出。11.2 验证裁剪结果grep^CONFIG_NET.config预期CONFIG_NET不再出现或显示# CONFIG_NET is not set。11.3 重新编译裁剪版内核make-j$(nproc)zImage dtbscparch/arm/boot/zImage ~/EmbeddedLinuxLab/build/lab02/zImage_trimmedcparch/arm/boot/zImage /mnt/EmbeddedLinuxLab/tftp/zImage_trimmed实测裁剪版 zImage 约 8.5 MB比完整版约 11 MB缩小约 23%。11.4 用同一 RootFS 启动裁剪版内核setenv kernel_addr_r 0x60100000setenv fdt_addr_r 0x62000000setenv ramdisk_addr_r 0x63000000tftpboot${kernel_addr_r}zImage_trimmedtftpboot${fdt_addr_r}vexpress-v2p-ca9.dtbtftpboot${ramdisk_addr_r}initramfs.gzsetenv bootargsconsolettyAMA0,38400n8 root/dev/ram0 rwbootz${kernel_addr_r}${ramdisk_addr_r}${fdt_addr_r}教学点即使 Linux 内核的网络子系统被完全裁掉U-Boot 仍能通过 TFTP 下载内核因为 U-Boot 的网络栈与 Linux 内核网络是两套独立实现。十二、实验结果对比项目完整内核裁剪内核无 CONFIG_NETzImage 大小10,904,064 bytes约 11 MB约 8.5 MB启动到 shell成功成功网络功能有无启动日志完整无网络驱动加载日志十三、实验结论Linux 内核移植的本质是在已有 ARMv7 支持基础上为目标板选择正确的配置、设备树和启动参数而不是重写内核。只有 Kernel 无法形成可交互系统必须配合 RootFS用户空间才能进入 shell。内核裁剪是“按需求删除功能”关闭 CONFIG_NET 后系统核心仍可启动到 shell但网络功能消失镜像体积明显缩小。U-Boot 网络与 Linux 内核网络相互独立裁掉 Linux 网络不影响 U-Boot 通过 TFTP 下载内核。十四、常见问题与排查现象可能原因排查方法串口无输出控制台设备节点缺失检查dev/console是否存在Kernel panic: VFSRootFS 未正确加载检查 bootargs 的root/dev/ram0initramfs 无法解压cpio 格式错误用cpio -H newc重新打包裁剪后无法启动误删了必要驱动恢复 config_full_backup 重新裁剪TFTP 下载超时网络配置错误检查 U-Boot 的ipaddr与serverip十五、实测记录表项目实测值完整内核编译时间约 2 小时zImage_full 大小10,904,064 bytesDTB 大小14,129 bytesinitramfs.gz 大小约 1.2 MB裁剪版 zImage 大小约 8.5 MB启动到 shell 时间约 5 秒