2. 嵌入式文件系统基础

在RK3568平台进行系统裁剪与优化之前,必须首先理解嵌入式文件系统的设计逻辑。文件系统不仅仅是数据的存储容器,它直接决定了系统的启动速度、运行稳定性、存储寿命以及OTA升级的可靠性。本章将围绕根文件系统结构标准(FHS)展开,深入剖析ext4、squashfs、ubifs与initramfs四种主流文件系统的内部机制与适用边界,并结合RK3568的硬件特性(eMMC、NAND、DDR)给出选型决策路径。

RK3568 文件系统选型决策与结构映射 硬件存储介质 eMMC / NAND / DDR 文件系统类型 ext4 / squashfs / ubifs / initramfs FHS 目录结构 /bin /etc /usr /var /data 业务场景 启动/OTA/数据 决策路径:存储介质 → 读写需求 → 压缩需求 → 启动阶段 → 文件系统选型 ext4 • 可读写,日志 • eMMC/SD 首选 • 适合 /data 分区 • 磨损均衡依赖MMC • 裁剪后约 2-5MB squashfs • 只读,高压缩 • 适合 /usr /opt • 启动快,防篡改 • gzip/lz4/xz • 适合只读rootfs ubifs • 专为NAND设计 • 磨损均衡/掉电保护 • 需要UBI层 • 适合裸NAND • 支持压缩与加密 initramfs • 内存中解压运行 • 用于早期初始化 • 加载驱动/挂载真实FS • 大小受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
经验提醒:在RK3568的Buildroot配置中,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设备支持。

风险提示:squashfs不支持写操作,如果应用程序试图写入/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),仅包含启动必需组件。

总结复盘:文件系统选型没有“银弹”,必须结合存储介质、读写需求、压缩比和启动时间综合权衡。在RK3568项目中,推荐组合方案:initramfs(早期引导) + squashfs(只读系统) + ext4(可写数据)。对于NAND方案,则使用initramfs + ubifs。通过FHS结构优化和文件系统裁剪,通常可以将系统总占用降低50%以上,同时提升启动速度与可靠性。