LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

OpenWRT(20):NAND上squashfs+ubifs的overlay实现rootfs

OpenWrt 使用 squashfs + ubifs 组合作为根文件系统(rootfs)的设计,是针对嵌入式设备(尤其是基于 NAND Flash 的硬件)的一种优化方案。这种设计结合了两种文件系统的优势,解决了嵌入式场景下的关键需求。

1 背景说明

  • 嵌入式设备的硬件限制
    • NAND Flash 特性:嵌入式设备(如路由器、IoT 设备)通常使用 NAND Flash 存储,但其存在以下挑战:
        坏块管理:NAND Flash 在生命周期中会出现坏块,需动态管理。擦写次数限制:每个存储单元的擦写次数有限(通常约 1~10 万次)。非对齐访问:需避免频繁的小文件写入,延长 Flash 寿命。
    • 资源受限:嵌入式设备存储空间有限,需高压缩率的文件系统。
  • OpenWrt 的需求
    • 固件可靠性:系统核心部分需保持稳定,避免因用户误操作或意外断电损坏。
    • 可写数据隔离:用户配置、日志等需持久化存储,但与只读系统分离。
    • 快速恢复:支持通过复位操作恢复出厂设置,同时保留核心系统不变。

2 镜像生成

OpenWrt 将根文件系统分为两部分:

  1. squashfs:作为只读的根文件系统,存储系统核心文件(内核、预装软件)。
  2. ubifs(在 rootfs_data 卷中):作为可写分区,存储用户配置、临时文件等,通常挂载为 OverlayFS 的顶层(实现写入覆盖)。

这两个文件系统通过 UBI(Unsorted Block Images) 框架管理,位于同一 MTD 分区中,但分为不同的 UBI 卷(Volume)。

这么实现具有如下优点与收益:

  • 可靠性提升
    • - 只读的 squashfs:
      • - 系统核心文件不可修改,避免因异常断电或错误操作导致系统损坏。
      • - 固件升级时只需替换整个 squashfs,操作原子性强,失败风险低。
    • - 崩溃恢复:若用户空间数据(ubifs)损坏,可通过删除 rootfs_data 卷快速恢复出厂设置,而无需重刷固件。
  • 存储效率优化
    • - 高压缩率:squashfs 支持多种压缩算法(如 LZMA、Zstd),显著减少固件体积,节省 Flash 空间。
    • - UBI 的磨损均衡:ubifs 基于 UBI,自动处理坏块管理和磨损均衡,延长 NAND 寿命。
  •  读写性能分离
    • - 只读操作:squashfs 的只读特性减少对 NAND 的写入压力。
    • - 高效写入:ubifs 针对 Flash 优化,支持日志(journaling)和快速随机写入,适合频繁变更的小文件(如配置文件)。
  • 灵活的 OverlayFS 机制
    • - OverlayFS 叠加:将 ubifs 挂载为 OverlayFS 的可写层,覆盖在 squashfs 之上,用户的所有修改(如安装软件、配置变更)仅影响 ubifs 层,而 squashfs 保持纯净。
    • - 空间复用:用户可动态扩展可写层,无需预先分配固定大小。
  •  兼容性与维护便利
    • - 固件升级简化:升级时只需更新 squashfs 镜像,用户数据(ubifs)独立保留。
    • - 调试友好:可通过重置 rootfs_data 快速排除软件配置问题,无需重刷固件。

在《OpenWRT(10):OpenWRT下rootfs的cpio/squashfs/ubifs/ext4生成流程》中介绍了生成rootfs.ubi的工具ubinize-image.sh,其配置文件ubinize.cfg如下:

[rootfs]  # 定义rootfs卷的配置。
mode=ubi  # 使用UBI文件系统模式。
vol_id=0  # 分区的ID。
vol_type=dynamic  # 动态卷,大小可以根据需要增长。
vol_name=rootfs  # 卷的名称。
image=root.squashfs  # 指定SquashFS镜像文件的路径。
vol_size=21471232  # 卷的大小。

[rootfs_data]  # 定义rootfs_data卷的配置。
mode=ubi  # 使用UBI文件系统模式。
vol_id=1  # 分区的ID。
vol_type=dynamic  # 动态卷,大小可以根据需要增长。
vol_name=rootfs_data  # 卷的名称,通常用于存储用户数据。
vol_size=1MiB  # 卷的大小,单位是MiB(1MB)。
vol_flags=autoresize  # 卷的标志,autoresize表示卷可以自动调整大小。

参考《[OpenWrt Wiki] The OpenWrt Flash Layout》,以root.squashfs作为rootfs volume的输入,创建一个空的rootfs_data volume。输出rootfs.ubi文件:

3 命令行配置

 一个bootargs示例如下:

Kernel command line: earlycon=... console=... ubi.mtd=rootfs,2048 ubi.block=0,rootfs root=/dev/ubiblock0_0 rootfstype=squashfs ro rootwait mtdparts=spi0.0:...,64m(rootfs),...

最终形成的squashfs+ubifs在NAND上实例如下:

 

解读如下:

  • 通过mtdparts可以知道:rootfs分区表用于存放rootfs。
  • ubi.mtd=rootfs,2048,表示第一个ubi对应rootfs分区。即ubi0。
  • ubi.block=0,rootfs,表示在ubi0上的rootfs volume创建ubiblock设备,对应/dev/ubiblock0_0。
  • root=/dev/ubiblock0_0表示使用rootfs分区的rootfs volume作为根文件系统。
  • rootfstype=squashfs表示rootfs的类型为squashfsh。

4 启动流程

 根据以上command line配置,启动流程如下:

  • Kernel进行command line中ubi.mtd指定的分区attach。autoresize的volume进行大小调整。
  • 对ubi.block配置生成ubiblock设备。
  • OpenWRT的preinit调用mount_root在rootfs分区的rootfs_data volume上创建文件系统:
UBIFS (ubi0:1): default file-system created
  • mount_root进行剩余的overlay等工作。

启动后mount:

/dev/root on /rom type squashfs (ro,relatime,errors=continue)
/dev/ubi0_1 on /overlay type ubifs (rw,noatime,assert=read-only,ubi=0,vol=1)
overlayfs:/overlay on / type overlay (rw,noatime,lowerdir=/,upperdir=/overlay/upper,workdir=/overlay/work,uid=on)
...

lowerdir是overlayfs的下层目录,这里指的是rootfs volume的squashfs挂载的根目录/。

upperdir是overlayfs的上层目录,这里指的是rootfs_data volume的ubifs挂载的根目录/overlay/upper。

workdir是overlayfs挂载时的工作目录,用于存储overlayfs操作的元数据和临时文件。workdir对用户是透明的,通常由overlayfs自动创建和管理。

5 内核相关配置

为了使能squashfs+ubifs的rootfs方案,需要如下配置:

Device Drivers
  Memory Technology Device (MTD) support
    Enable UBI - Unsorted block images--支持UBI。
      Read-only block devices on top of UBI volumes--支持基于UBI的block设备。
File systems
  Overlay filesystem support--支持overlayfs。
  Miscellaneous filesystems
    UBIFS file system support--支持ubifs。
    SquashFS 4.0 - Squashed file system suport--支持squashfs。

6 其他问题

6.1  为什么ubifs volume可以用/dev/ubiX_Y,而squashfs不行?

在UBI卷上挂载UBIFS(Unsorted Block Image File System)时,可以直接通过UBI字符设备(如/dev/ubiX_Y)进行挂载,这与SquashFS需要通过UBI块设备(/dev/ubiblockX_Y)挂载的行为不同。这种差异源于UBIFS与UBI的紧密集成设计以及文件系统与存储管理的分工不同。以下是具体原因:

1.UBIFS是专为UBI设计的文件系统

UBIFS是专门针对UBI(Unsorted Block Images)卷设计的文件系统,其核心设计目标是直接利用UBI的特性(如坏块管理、磨损均衡、逻辑擦除块映射等)。因此:

  • 直接操作UBI卷:UBIFS不需要通过块设备抽象层(如ubiblock),而是直接与UBI子系统交互,通过UBI字符设备(/dev/ubiX_Y)访问物理闪存。
  • 感知UBI元数据:UBIFS能够理解UBI卷的布局(例如逻辑擦除块LEB与物理擦除块PEB的映射),从而直接管理存储空间,而无需依赖块设备的中间层。

2.UBI字符设备提供了UBIFS所需的功能

UBI字符设备(/dev/ubiX_Y)虽然是字符设备接口,但它隐含了UBI的管理逻辑,包括:

  • 逻辑擦除块(LEB)的访问:UBIFS通过UBI字符设备直接读写逻辑擦除块,UBI子系统会自动处理坏块替换和磨损均衡。
  • 原子写入支持:UBI提供了原子操作(如UBI_IOCVOLUP),UBIFS利用这些特性实现崩溃安全性(例如通过UBIFS的日志机制)。
  • 卷更新接口:UBIFS在挂载时会通过UBI字符设备直接验证卷的元数据(如超级块),而无需块设备的文件系统格式解析。

3.UBIFS与块设备抽象(ubiblock)的无关性

UBIFS的设计目标是直接管理UBI卷,而不是依赖传统的块设备抽象:

  • 无需块设备缓存:UBIFS自身实现了高效的缓存机制(如Page Cache和日志缓存),直接通过UBI字符设备访问闪存。
  • 直接处理物理特性:UBIFS需要感知闪存的擦除块大小、最小I/O单元等物理特性,这些特性由UBI子系统通过字符设备接口传递,而块设备会隐藏这些细节(导致UBIFS无法通过块设备正确操作)。

4.对比:SquashFS为何需要块设备

与UBIFS不同,SquashFS是一个通用只读文件系统,其设计不针对UBI或闪存:

  • 依赖块设备接口:SquashFS需要通过标准的块设备接口(如/dev/ubiblockX_Y)访问数据,因为它无法直接处理UBI的逻辑擦除块映射或坏块管理。
  • 无UBI感知能力:SquashFS假设底层存储是连续的、无坏块的块设备,而UBI字符设备无法满足这一假设(需要通过ubiblock将其转换为“虚拟”的可靠块设备)。

5.技术实现差异的总结

特性 UBIFS SquashFS
设计目标 专为UBI/闪存优化 通用的只读压缩文件系统
底层接口 直接使用UBI字符设备(/dev/ubiX_Y) 依赖UBI块设备(/dev/ubiblockX_Y)
存储管理 直接利用UBI的坏块管理和磨损均衡 假设底层块设备是可靠的(需ubiblock保障)
缓存与I/O调度 自行实现缓存,直接操作UBI 依赖内核块层缓存和调度
原子操作与崩溃安全 通过UBI原子操作实现 无此需求(只读)

UBIFS可以直接通过UBI字符设备挂载,是因为它专为UBI设计,能够直接利用UBI的存储管理功能(坏块处理、磨损均衡),无需块设备抽象。而SquashFS必须通过UBI块设备挂载,因为它是一个通用文件系统,依赖块设备接口的可靠性和标准性。这种差异体现了文件系统与底层存储管理层的不同分工。

由于squashfs在UBI volume中,所以无法直接使用/dev/mtdx或者/dev/mtdblockx设备。

所以rootfs volume使用块设备/dev/ubiblockX_Y挂载;rootfs_data volume使用字符设备/dev/ubiX_Y挂载

posted on 2025-02-01 23:59  ArnoldLu  阅读(884)  评论(0)    收藏  举报

导航