嵌入式存储文件系统:ext4、jffs2、ubifs 格式选型与适配

选文件系统就像选鞋子——跑步穿跑鞋,登山穿登山靴。把 ext4 硬塞进 NOR Flash,就像穿高跟鞋踢足球,不是不行,是疼。

引言

嵌入式设备的存储介质种类繁多:SD 卡、eMMC、NOR Flash、NAND Flash……每种介质的特性不同,对应的文件系统也不同。选错文件系统,轻则性能拉胯,重则数据丢失。本文将对比主流嵌入式文件系统格式,给出选型建议和适配方法。

一、嵌入式存储介质分类

在选文件系统之前,先搞清楚存储介质的类型:

介质类型特点典型容量常见用途
NOR Flash读取快、随机访问、XIP可执行1~32MB存放Bootloader、内核
NAND Flash密度高、便宜、有坏块128MB~4GB存放根文件系统、用户数据
eMMC内置控制器、无坏块问题4~64GB手机、平板、开发板
SD/TF卡可插拔、便携4~128GB开发板、便携设备
SPI Flash接口简单、容量小4~32MB小型IoT设备

关键区别:NOR/NAND Flash 是裸芯片,需要文件系统处理坏块和磨损均衡;eMMC/SD 卡内置了 FTL(Flash Translation Layer)控制器,对上层透明。

二、主流文件系统格式对比

2.1 全面对比表

特性ext4JFFS2UBIFSSquashFSYAFFS2
支持介质eMMC/SDNOR/NANDNAND任意NAND
可写性读写读写读写只读读写
掉电安全否(需journal)是是N/A是
磨损均衡不需要(FTL处理)内置UBI层处理不需要内置
坏块管理不需要(FTL处理)内置UBI层处理不需要内置
压缩否是(zlib)是(lzo/zlib)是(xz/gzip)否
挂载速度快慢(全扫描)快(索引)快中
内存占用低高(全加载)中低中
适用容量任意<128MB<2GB任意<2GB

2.2 各格式详解

ext4

桌面 Linux 的主力文件系统。在嵌入式中主要用于 eMMC 和 SD 卡。

  • 优点:成熟稳定、性能好、工具链完善
  • 缺点:不支持掉电安全(虽然有 journal,但嵌入式突然断电仍有风险)
  • 适用:eMMC、SD 卡,资源相对充足的设备
JFFS2

专为 Flash 设计的日志结构文件系统。

  • 优点:掉电安全、支持压缩、支持 NOR 和 NAND
  • 缺点:挂载速度随容量线性增长(128MB 需要 10+ 秒),内存占用大
  • 适用:小容量 NOR Flash(<32MB),小容量 NAND
UBIFS

JFFS2 的继任者,基于 UBI 子系统,专为大容量 NAND Flash 优化。

  • 优点:挂载快(维护索引树)、支持压缩、掉电安全
  • 缺点:配置复杂(需要先创建 UBI 卷)
  • 适用:大容量 NAND Flash(128MB~2GB)
SquashFS

只读压缩文件系统,适合存放不需要修改的系统文件。

  • 优点:压缩率高(xz压缩可节省60%+空间)、挂载快
  • 缺点:只读,无法修改
  • 适用:根文件系统的只读部分、overlayfs 的底层

三、选型决策流程

                  存储介质是什么?
                       │
          ┌────────────┼────────────┐
          │            │            │
       eMMC/SD    NAND Flash    NOR Flash
          │            │            │
      ext4(首选)   容量>128MB?    容量<32MB?
          │         /    \         /    \
         否       是      否      是      否
          │       │       │       │       │
        SquashFS  UBIFS  JFFS2   JFFS2   SquashFS
        (只读层)                        (只读层)

选型原则总结

  1. eMMC/SD 卡 → ext4(读写)或 squashfs(只读)
  2. 大容量 NAND → UBIFS
  3. 小容量 NAND → JFFS2
  4. NOR Flash → JFFS2(小容量)或 SquashFS(只读)
  5. 需要只读保护 → SquashFS + OverlayFS(上层可写)

四、各文件系统适配方法

4.1 ext4 适配

# 1. 创建 ext4 镜像
dd if=/dev/zero of=rootfs.ext4 bs=1M count=256
mkfs.ext4 -L rootfs rootfs.ext4

# 2. 挂载并填充内容
mkdir -p /mnt/rootfs
sudo mount -o loop rootfs.ext4 /mnt/rootfs
sudo cp -a _install/* /mnt/rootfs/
sudo umount /mnt/rootfs

# 3. 烧录到 SD 卡分区
sudo dd if=rootfs.ext4 of=/dev/sdb2 bs=1M

内核 bootargs 配置:

root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw

4.2 JFFS2 适配

JFFS2 镜像使用 mkfs.jffs2 工具制作:

# 1. 安装 mtd-utils
sudo apt install mtd-utils

# 2. 制作 JFFS2 镜像
#    -p 0x1000000: 镜像大小16MB
#    -r rootfs/: 源目录
#    -o rootfs.jffs2: 输出文件
#    --pad: 填充到指定大小
mkfs.jffs2 \
    -s 0x800 \
    -e 0x20000 \
    -p 0x1000000 \
    -d rootfs/ \
    -o rootfs.jffs2

# 参数说明:
# -s: 页大小(NAND页大小,2048字节=0x800)
# -e: 擦除块大小(128KB=0x20000)
# -p: 填充到指定大小
# -d: 源目录
# -o: 输出文件

内核 bootargs 配置:

root=/dev/mtdblock2 rootfstype=jffs2 rw

4.3 UBIFS 适配

UBIFS 的制作流程比 JFFS2 复杂,需要两步:先制作 UBIFS 镜像,再封装为 UBI 镜像。

# 1. 制作 UBIFS 镜像
#    -m 2048: 最小IO大小(NAND页大小)
#    -e 129024: 擦除块大小(128KB减去UBI头开销)
#    -c 2048: 最大逻辑擦除块数
#    -r rootfs/: 源目录
mkfs.ubifs \
    -m 2048 \
    -e 129024 \
    -c 2048 \
    -r rootfs/ \
    -o rootfs.ubifs

# 2. 封装为 UBI 镜像
#    需要创建 ubifs.ini 配置文件
cat > ubifs.ini << 'EOF'
[ubifs]
mode=ubi
image=rootfs.ubifs
vol_id=0
vol_size=256MiB
vol_type=dynamic
vol_name=rootfs
vol_alignment=1
vol_flags=autoresize
EOF

#    生成 UBI 镜像
ubinize \
    -m 2048 \
    -p 128KiB \
    -s 2048 \
    -O 2048 \
    -o rootfs.ubi \
    ubifs.ini

关键参数计算:

参数含义计算方式
-m最小IO大小= NAND 页大小
-e逻辑擦除块大小= 物理擦除块 - 2×(页大小+8)
-c最大LEB数= 分区大小 / 逻辑擦除块大小
-p物理擦除块大小= NAND 擦除块大小

内核 bootargs 配置:

ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs rw

4.4 SquashFS + OverlayFS 适配

这种方案下,底层 SquashFS 提供只读的系统文件,上层 OverlayFS 提供可写层。常见于 OpenWrt 等路由器系统。

# 1. 制作 SquashFS 镜像
mksquashfs rootfs/ rootfs.squashfs \
    -comp xz \
    -no-xattrs \
    -no-fragments

# 2. 烧录到 Flash 分区
#    Flash 分区布局:
#    | u-boot | kernel | rootfs(squashfs) | overlay(jffs2) |

# 3. 内核启动后,用 overlayfs 合并可写层
mount -t squashfs /dev/mtdblock2 /rom
mount -t jffs2 /dev/mtdblock3 /overlay
mount -t overlay overlay \
    -o lowerdir=/rom,upperdir=/overlay/upper,workdir=/overlay/work \
    /mnt

五、NAND Flash 适配注意事项

5.1 分区划分

NAND Flash 通常在设备树中定义分区:

&nand {
    partition@0 {
        label = "bootloader";
        reg = <0x000000 0x100000>;   /* 1MB */
    };
    partition@100000 {
        label = "kernel";
        reg = <0x100000 0x500000>;   /* 5MB */
    };
    partition@600000 {
        label = "rootfs";
        reg = <0x600000 0x2000000>;  /* 32MB */
    };
    partition@2600000 {
        label = "userdata";
        reg = <0x2600000 0x1A00000>; /* 26MB */
    };
};

5.2 硬件 ECC 配置

NAND Flash 需要配置 ECC(Error Correction Code)校验方式:

# 查看NAND Flash信息
cat /proc/mtd

dev: size erasesize name
mtd0: 04000000 00020000 "nand-flash"
mtd1: 00100000 00020000 "bootloader"
mtd2: 00500000 00020000 "kernel"
mtd3: 02000000 00020000 "rootfs"

总结

文件系统选型的核心原则:存储介质决定文件系统类型。eMMC/SD 用 ext4,NAND 用 UBIFS 或 JFFS2,NOR 小容量用 JFFS2。如果需要只读保护,SquashFS + OverlayFS 是最佳组合。选对文件系统只是第一步,正确配置分区参数(页大小、擦除块大小、LEB数量)才是成功适配的关键。下一阶段就是打包烧录,让系统真正在板子上跑起来。

更多推荐