2. 嵌入式文件系统基础
在RK3568平台进行系统裁剪与优化之前,必须首先理解嵌入式文件系统的设计逻辑。文件系统不仅仅是数据的存储容器,它直接决定了系统的启动速度、运行稳定性、存储寿命以及OTA升级的可靠性。本章将围绕根文件系统结构标准(FHS)展开,深入剖析ext4、squashfs、ubifs与initramfs四种主流文件系统的内部机制与适用边界,并结合RK3568的硬件特性(eMMC、NAND、DDR)给出选型决策路径。
2.1 FHS:嵌入式根文件系统的骨架与变通
核心原理:FHS(Filesystem Hierarchy Standard)定义了Linux系统中目录的存放规则,其目的是让软件包、脚本和管理工具能够预测文件位置。在嵌入式环境中,我们并不需要完整照搬桌面版的全部目录,但必须保留关键骨架。RK3568的rootfs通常包含以下必备目录:/bin(用户命令)、/sbin(系统管理命令)、/etc(配置文件)、/lib(共享库)、/usr(用户程序与库)、/var(可变数据)、/tmp(临时文件)、/proc与/sys(内核虚拟文件系统挂载点)、/dev(设备节点)。此外,针对嵌入式场景,我们通常增加/data或/userdata用于存放用户可写数据,以及/mnt用于外部存储挂载。
判断方法:在裁剪时,应遵循“最小必要目录”原则。使用busybox作为核心工具集时,/bin与/sbin可以合并为符号链接。通过find命令扫描rootfs中所有文件,按目录统计大小,找出占用空间最大的目录进行重点优化。例如,/usr/share下的locale、doc、man页面通常可以安全删除。
| 目录 | 嵌入式必要性 | 裁剪建议 |
|---|---|---|
| /bin, /sbin | 必需 | 使用busybox合并,仅保留必要applet |
| /lib | 必需 | 使用arm-linux-gnueabihf-strip去除符号表 |
| /usr/share | 可选 | 删除doc、man、locale(保留en_US.UTF-8) |
| /var | 必需 | 挂载为tmpfs,日志用logrotate限制大小 |
| /data | 按需 | 独立分区,使用ext4或ubifs |
BR2_ROOTFS_OVERLAY可以指定一个目录,将自定义配置(如/etc/inittab、/etc/fstab)覆盖到rootfs中,这是保持FHS结构整洁的常用手段。2.2 常用文件系统类型深度对比
核心原理:RK3568支持从eMMC、SD卡、NAND、SPI NOR启动,不同介质对文件系统的要求截然不同。ext4是eMMC的默认选择,因为它支持日志、扩展属性和在线扩容;squashfs是只读压缩文件系统,适合存放不会变化的系统程序;ubifs专为裸NAND设计,具备磨损均衡和掉电恢复能力;initramfs则是一个cpio归档,由内核解压到内存中,用于早期初始化。
判断方法:选型时需回答三个问题:① 该分区是否需要写入?② 存储介质是eMMC还是NAND?③ 是否需要压缩以节省空间?如果分区只读且希望最大化压缩率,squashfs是最优解;如果分区需要频繁读写且介质为eMMC,ext4更稳妥;如果介质是NAND且需要读写,则必须使用ubifs(或jffs2,但ubifs更现代)。initramfs则用于启动早期阶段,通常只有2-8MB。
# 在RK3568 Buildroot中启用squashfs
BR2_TARGET_ROOTFS_SQUASHFS=y
BR2_TARGET_ROOTFS_SQUASHFS4_LZ4=y # 使用lz4压缩,解压速度快
# 启用ubifs(需要配合UBI)
BR2_TARGET_ROOTFS_UBIFS=y
BR2_TARGET_UBIFS_LEB_SIZE=0x1F000
BR2_TARGET_UBIFS_MIN_IO_SIZE=2048
案例拆解:某工业HMI项目使用RK3568,存储为8GB eMMC。初始方案将整个rootfs放在ext4分区,启动时间约3.2秒。优化后,将/usr和/opt(存放Qt应用与库)打包为squashfs只读分区,/etc和/var保留在ext4可写分区,/data独立ext4分区。结果:rootfs从1.2GB降至420MB,启动时间缩短至2.1秒,且系统只读部分避免了意外篡改。关键操作是使用mksquashfs命令生成镜像,并在内核中启用loop设备支持。
/usr下的文件(例如某些软件在运行时生成配置文件),会导致只读文件系统错误。务必在应用层将可写路径重定向到/var或/data。2.3 initramfs与根文件系统的挂载流程
核心原理:RK3568的启动流程为:BootROM → U-Boot → Kernel → initramfs → 挂载真实rootfs。initramfs是一个小型的cpio归档,内核将其解压到tmpfs(基于DDR)中,执行/init脚本。该脚本负责加载必要的驱动(如eMMC、NAND驱动)、创建设备节点、然后通过switch_root或pivot_root切换到真正的根文件系统。
操作流程:在Buildroot中,启用BR2_TARGET_ROOTFS_CPIO生成initramfs。其内容通常包含:busybox(静态编译)、modprobe、必要的内核模块(如dw_mmc、phy-rockchip)、mdev或udev(设备管理)、以及fstab。关键点在于/init脚本的逻辑:先挂载/proc和/sys,然后使用mdev -s扫描设备,最后挂载真实rootfs。
#!/bin/sh
# /init 脚本示例(精简版)
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
# 等待eMMC设备出现
while [ ! -e /dev/mmcblk0p3 ]; do sleep 0.1; done
mount -t ext4 /dev/mmcblk0p3 /mnt/rootfs
exec switch_root /mnt/rootfs /sbin/init
常见误区:很多开发者将initramfs与initrd混淆。initrd是块设备镜像,需要驱动支持才能读取;而initramfs是内存中的cpio归档,无需块设备驱动。在RK3568上,推荐使用initramfs,因为它更简单且启动更快。另一个误区是试图在initramfs中放入完整的文件系统,这会导致DDR占用过高。正确的做法是保持initramfs最小化(建议小于8MB),仅包含启动必需组件。
initramfs(早期引导) + squashfs(只读系统) + ext4(可写数据)。对于NAND方案,则使用initramfs + ubifs。通过FHS结构优化和文件系统裁剪,通常可以将系统总占用降低50%以上,同时提升启动速度与可靠性。