01嵌入式 Linux 从零构建全栈手册 (基于 Vexpress-A9)
📘 嵌入式 Linux 从零构建全栈手册 (基于 Vexpress-A9)
目录索引:
- 环境搭建:地基与工具链
- U-Boot 移植:编译、裁剪与设备树命令开启
- Linux 内核:源码下载、配置、编译与“防卡死”处理
- 根文件系统:BusyBox 静态编译与 Init 脚本
- 应用开发:编写 C 程序并植入系统
- SD 卡持久化:分区、格式化与脱机运行
第零章:环境搭建 (一切的开始)
我们要在一个 x86 的 PC (Ubuntu) 上,生成能跑在 ARM 开发板上的代码。
0.1 安装基础工具
打开 Ubuntu 终端,执行:
# 1. 更新软件源 |
0.2 创建工作目录结构
为了防止文件乱放,我们需要统一规划。
# 回到家目录 |
第一章:Bootloader (U-Boot)
U-Boot 是系统的“第一棒”。它的核心任务是初始化 DDR 内存,并将内核搬运到内存中。
1.1 下载源码
cd ~/Embedded_study |
1.2 配置与裁剪 (关键步骤)
我们需要开启 fdt 命令,否则后续无法查看和操作设备树。
应用板级默认配置:
# 指定架构和编译器
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 加载 Vexpress 开发板配置
make vexpress_ca9x4_defconfig强制开启 fdt 命令 (最稳妥的方法):
- 直接修改配置文件,防止在 Menuconfig 里找不着。
# 打开配置文件
nano .config- 按
Ctrl+W搜索CONFIG_CMD_FDT。 - 如果找不到,直接滑到文件最后一行,新起一行输入:
CONFIG_CMD_FDT=y
- 按
Ctrl+O回车保存,Ctrl+X退出。
1.3 编译 U-Boot
# -j8 表示用 8 个线程编译,速度快 |
- 产物:编译完成后,当前目录下会生成
u-boot(带调试信息) 和u-boot.bin(二进制)。
1.4 启动与设备树实验 (核心原理)
我们要在 U-Boot 里验证它是否能读取设备树。
启动 QEMU:
qemu-system-arm -M vexpress-a9 -m 512M -nographic -kernel u-boot
寻找设备树地址 (原理):
U-Boot 启动时会把内置的设备树(dtb)放在内存的某个位置。我们需要找到它。
在=>提示符下输入:bdinfo
- 现象:输出中有一行
fdt_blob或fdt address,记录后面的十六进制地址(例如0x608xxxxx或0x7fxxxxxx)。
- 现象:输出中有一行
挂载设备树:
假设你上一步看到的地址是0x6087d8b0(请替换为你实际看到的):fdt addr 0x6087d8b0
- 原理:告诉 fdt 命令工具,“去这个内存地址解析数据”。
查看内容:
fdt print /
- 现象:屏幕打印出类似于 JSON 的树状结构。
- 意义:证明 U-Boot 功能正常,且具备解析硬件描述的能力。
退出 QEMU:按
Ctrl+A,松开,按x。
第二章:Linux 内核 (Kernel)
内核是系统的“大脑”。我们要下载源码,并解决两个著名的坑:“指令集不支持”和“启动卡死”。
2.1 下载源码 (v5.15)
cd ~/Embedded_study |
2.2 配置内核
我们需要开启 Early Printk,否则内核启动时如果出错,会直接卡死且没有任何打印(沉默死亡)。
生成默认配置:
# 显式指定架构,防止环境变量失效
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig进入菜单开启调试:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
- 操作路径:
Kernel hacking—>Kernel low-level debugging functions(按 Y 选中)Early printk(按 Y 选中)
- 保存并退出。
- 操作路径:
2.3 编译内核 (防坑指南)
Ubuntu 新版 GCC 可能会把代码编译成旧版 ARM 指令,导致报错。我们必须强制指定指令集。
# zImage: 压缩内核 |
- 产物:
- 内核:
arch/arm/boot/zImage - 设备树:
arch/arm/boot/dts/vexpress-v2p-ca9.dtb
- 内核:
第三章:根文件系统 (RootFS)
内核启动后需要运行程序,这些程序存放在文件系统里。我们用 BusyBox 制作一个最小系统。
3.1 下载与解压
cd ~/Embedded_study |
3.2 静态编译配置 (至关重要)
如果不静态编译,程序在板子上运行时会找不到库文件(Error -2)。
- 打开配置菜单:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
- 设置静态链接:
- 进入
Settings—> - 找到
Build static binary (no shared libs),按Y选中变成[*]。 - 保存退出。
- 进入
3.3 编译与安装
# 编译 |
3.4 完善文件系统结构
cd _install |
3.5 编写 Init 脚本 (PID 1)
内核启动的第一个程序。
# 创建 init 文件 |
填入以下内容:
|
赋予权限:
chmod +x init |
第四章:应用开发 (用户程序)
我们要写一个 C 程序,让它开机自动运行。
4.1 编写与交叉编译
cd ~ |
内容:
|
编译 (必须静态):
arm-linux-gnueabihf-gcc main.c -o lemon_app -static |
4.2 植入文件系统
把这个程序放到我们刚才做的 RootFS 里。
cp ~/main.c ~/Embedded_study/busybox-1.36.1/_install/bin/lemon_app |
4.3 修改 Init 脚本调用它
修改 ~/Embedded_study/busybox-1.36.1/_install/init,在 exec /bin/sh 之前加入:
/bin/lemon_app |
第五章:打包与网络启动 (集成测试)
5.1 制作 uRamdisk (RootFS 镜像)
U-Boot 需要特定格式的文件系统镜像。
cd ~/Embedded_study/busybox-1.36.1/_install |
5.2 搬运内核与设备树
把编译好的内核和设备树也放到 TFTP 目录。
cd ~/Embedded_study |
检查点:ls -l ~/tftp_root/ 应该有 zImage, vexpress-v2p-ca9.dtb, uRamdisk 三个文件。
5.3 启动 QEMU (网络模式)
cd ~/Embedded_study/uboot_study/u-boot |
5.4 U-Boot 启动命令
在 U-Boot => 下依次输入:
- 下载文件 (地址要隔开,防止覆盖):
tftp 0x60100000 zImage
tftp 0x61000000 uRamdisk
tftp 0x62000000 vexpress-v2p-ca9.dtb - 设置启动参数 (告诉内核根文件系统在内存 ram0):
setenv bootargs 'console=ttyAMA0,115200n8 debug earlyprintk root=/dev/ram0 rdinit=/init'
- 启动:
bootz 0x60100000 0x61000000 0x62000000
成功标志:看到 Welcome to My Embedded Linux! 和 [APP] Hello... 的打印。
第六章:SD 卡持久化 (最终形态)
网络启动断电数据就丢了。我们要把系统装进“虚拟 SD 卡”。
6.1 制作 SD 卡镜像
退出 QEMU,回到 Ubuntu。
创建空文件 (512MB):
cd ~/Embedded_study
dd if=/dev/zero of=sd.img bs=1M count=512分区 (fdisk):
fdisk sd.img
- 输入
n->p->1->2048->+64M(创建分区1) - 输入
n->p->2->回车->回车(创建分区2) - 输入
t->1->c(修改分区1类型为 FAT32) - 输入
w(保存)
- 输入
格式化:
# 关联回环设备
sudo losetup -f --show -P sd.img
# 假设输出 /dev/loop0
# 格式化 P1 (Boot, FAT32)
sudo mkfs.vfat -F 32 -n "BOOT" /dev/loop0p1
# 格式化 P2 (RootFS, EXT4)
sudo mkfs.ext4 -L "ROOTFS" /dev/loop0p2
6.2 烧写文件
# 挂载 |
6.3 SD 卡启动验证
启动 QEMU (插入 SD 卡):
cd ~/Embedded_study/uboot_study/u-boot
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-nographic \
-kernel u-boot \
-sd ~/Embedded_study/sd.imgU-Boot 操作:
# 1. 初始化 MMC
mmc rescan
# 2. 从 SD 卡读取文件到内存
# load mmc <设备>:<分区> <地址> <文件名>
load mmc 0:1 0x60100000 zImage
load mmc 0:1 0x62000000 vexpress-v2p-ca9.dtb
# 3. 设置启动参数 (关键修改)
# root=/dev/mmcblk0p2: 告诉内核根文件系统在 SD 卡第 2 分区
# rootfstype=ext4: 文件系统类型
# rw: 可读写
setenv bootargs 'console=ttyAMA0,115200n8 debug earlyprintk root=/dev/mmcblk0p2 rootfstype=ext4 rw init=/init'
# 4. 启动 (中间没有 Ramdisk 了,用 - 代替)
bootz 0x60100000 - 0x62000000持久化验证:
进入系统后,执行:echo "Data Saved!" > /root/test.txt
强制重启 QEMU,再次进入系统,执行
cat /root/test.txt,如果内容还在,说明全栈开发实战圆满成功!