liwen01 2024.07.21
前言
UBI (Unsorted Block Images)文件系统是一种用于裸 flash 的文件系统管理层。它是专为管理原始闪存设备而设计,特别适用于嵌入式系统。与 YAFFS2 和 JFFS2 不同的是,它可以提供整个 flash 空间的磨损平衡,并且有良好的扩展性,适用于大容量的 nand flash 。
(一)MTD、UBI 与 UBIFS
前面介绍的 JFFS2 和 YAFFS2 都是运行在 MTD 之上,而 UBIFS 只能运行在 UBI 之上,UBI 又只能运行于 MTD 之上,所以这里就涉及到 3 个子系统:MTD、UBI、UBIFS。
- MTD 提供了对底层闪存硬件的抽象和基本管理。
- UBI 在 MTD 之上增加了一层管理,处理闪存的复杂性并提供逻辑卷管理。
- UBIFS 是在 UBI 卷上运行的文件系统,充分利用 UBI 的特性,提供高效可靠的文件存储。
这里需要特别注意,这里所说的闪存,是指裸 flash,而不是经过FTL转换后的 U 盘、SD、TF、SSD 等设备。
在 Linux 中,经过 FTL 转换后的 U 盘、SD、TF、SSD 等设备,它们属于块设备,是模拟传统磁盘设计的一种数据结构,以扇区 sector 为读写单位。
而 MTD,它既不是字符设备,也不是块设备,它只是 MTD 设备
(1) MTD (Memory Technology Device)
MTD 是 Linux 内核中的一个子系统,用于支持不同类型的闪存设备,如 NOR Flash 和 NAND Flash。MTD 提供了一个抽象层,使得文件系统和用户空间程序可以方便地访问底层的闪存硬件。
- MTD 设备:在 Linux 系统中,MTD 设备通常以 /dev/mtdX 和 /dev/mtdblockX 的形式出现,其中 X 是设备编号。
- MTD 子设备:一个 MTD 设备可以被划分为多个子设备,每个子设备可以独立使用。
(2)UBI (Unsorted Block Images)
UBI 是一个在 MTD 设备之上的管理层,专门为 NAND Flash 设计。UBI 处理了 NAND Flash 固有的一些复杂性,如坏块管理和磨损均衡(wear leveling)。UBI将闪存划分为逻辑擦除块,并对它们进行管理。
- 坏块管理:UBI 能够检测和管理坏块,确保数据写入时不会使用坏块。
- 磨损均衡:UBI 通过均匀分布擦写操作,延长闪存的使用寿命。
- 逻辑卷:UBI 支持在 MTD 设备上创建多个逻辑卷,每个卷可以独立使用。
(3)UBIFS (UBI File System)
UBIFS 是专门为 UBI 设计的文件系统,直接在 UBI 卷上运行。UBIFS 充分利用 UBI 的功能,提供了高效和可靠的文件存储解决方案。
- 动态特性:UBIFS 支持动态调整文件系统大小,根据需要分配和回收空间。
- 日志结构:UBIFS 使用日志结构文件系统,减少数据损坏的风险并提高写入性能。
- 压缩:UBIFS 支持多种压缩算法,节省存储空间。
UBIFS 并不是唯一可以在UBI上运行的文件系统,理论上绝大部分文件系统都可以在 UBI 上运行。除了 UBIFS,其它文件系统在 UBI 上使用效率都不高。
(二)镜像文件制作
(1)UBIFS 镜像文件制作
(a)准备测试文件
新建4个测试目录,在目录中创建测试问价,文件使用 /dev/urandom 写入随机数
biao@ubuntu:~/test/ubifs/ubifs_urandom$ tree . ├── test1 │ ├── file1 │ ├── file1_1 │ └── file1_2 ├── test2 │ ├── file2 │ ├── file2_1 │ └── file2_2 ├── test3 │ ├── file3 │ ├── file3_1 │ └── file3_2 └── test4 ├── file4 ├── file4_1 └── file4_2 4 directories, 12 files biao@ubuntu:~/test/ubifs/ubifs_urandom$
文件大小信息如下:
biao@ubuntu:~/test/ubifs$ du ubifs_urandom 1904 ubifs_urandom/test3 168 ubifs_urandom/test2 1504 ubifs_urandom/test1 1476 ubifs_urandom/test4 5056 ubifs_urandom biao@ubuntu:~/test/ubifs$
(b)制作 UBIFS 镜像文件
mkfs.ubifs -r ubifs_urandom -m 2048 -e 129024 -c 10000 -o ubifs_urandom.ubifs
各参数的作用:
-r, -d, --root=DIR build file system from directory DIR -m, --min-io-size=SIZE minimum I/O unit size -e, --leb-size=SIZE logical erase block size -c, --max-leb-cnt=COUNT maximum logical erase block count -o, --output=FILE output to FILE
上面命令的作用是:将 ubifs_urandom 目录里面的文件打包成一个页大小为 2048(2KB)、逻辑擦除块大小为 129024(126KB)、最大逻辑块为 10000 的 UBIFS 镜像文件 (ubifs_urandom.ubifs)。
这里有几点需要注意:
- -m 是页大小,不是子页大小
- -e 设置的逻辑擦除块大小要与UBI里的相同,不然挂载的时候会报错误
(2)UBI 镜像文件制作
在 UBIFS 的基础上,制作一个UBI镜像文件
(a)制作 UBI 镜像配置文件
创建配置文件 ubinize.cfg
[ubifs] mode=ubi image=ubifs_urandom.ubifs vol_id=0 vol_size=256MiB vol_type=dynamic vol_name=ubifs_urandom vol_flags=autoresize
- image 为我们上面制作的UBIFS文件系统镜像文件
- vol_id 指定卷 ID,这个是在有多个卷的时候使用
- vol_size 定义卷的大小
- vol_type 设置为动态卷,卷的大小可以变化
- vol_name 卷的名字
- vol_flags 自动调整大小
(b)制作 UBI 镜像文件
ubinize -o ubi.img -m 2048 -O 512 -p 128KiB ubinize.cfg
UBI 镜像文件和 UBIFS 的镜像文件,都需要根据实际 Flash 的参数进行设置
上面命令的作用是:将一个 ubifs 镜像文件制作成一个页大小为 2048(2KB)、子页大小为 (256Byte),物理擦除块大小为 128KB 的 UBI 镜像文件。
(三) 挂载 UBIFS 文件系统
为了方便调试,我们这里直接使用PC机上的虚拟 MTD 设备来仿真Flash。Linux 内核中有 3 种 MTD 设备模拟器可用:
- mtdram:在 RAM 中模拟 NOR 闪存;
- nandsim:在 RAM 中模拟 NAND 闪存;
- block2mtd:在块设备上模拟 NOR 闪存。
(1)加载 nandsim 模块
这里仿一个 1GiB, 2048 bytes page的 nand flash。
sudo modprobe nandsim first_id_byte=0xec second_id_byte=0xd3 third_id_byte=0x51 fourth_id_byte=0x95
可以通过 /proc/mtd 和 /dev/mtd0 查看模拟的 nandflsh 信息
biao@ubuntu:~/test/ubifs$ cat /proc/mtd dev: size erasesize name mtd0: 40000000 00020000 "NAND simulator partition 0" biao@ubuntu:~/test/ubifs$ mtdinfo /dev/mtd0 mtd0 Name: NAND simulator partition 0 Type: nand Eraseblock size: 131072 bytes, 128.0 KiB Amount of eraseblocks: 8192 (1073741824 bytes, 1024.0 MiB) Minimum input/output unit size: 2048 bytes Sub-page size: 512 bytes OOB size: 64 bytes Character device major/minor: 90:0 Bad blocks are allowed: true Device is writable: true biao@ubuntu:~/test/ubifs$
上面加载 nandsim 的时候,有定义4个ID值,具体值需要根据芯片手册的数据来设置。
第一字节为制造商代码、第二字节为设备代码、第三、四字节为Flash特定参数
下面是几个示例:
modprobe nandsim first_id_byte=0x20 second_id_byte=0x33 - 16MiB, 512 bytes page; modprobe nandsim first_id_byte=0x20 second_id_byte=0x35 - 32MiB, 512 bytes page; modprobe nandsim first_id_byte=0x20 second_id_byte=0x36 - 64MiB, 512 bytes page; modprobe nandsim first_id_byte=0x20 second_id_byte=0x78 - 128MiB, 512 bytes page; modprobe nandsim first_id_byte=0x20 second_id_byte=0x71 - 256MiB, 512 bytes page; modprobe nandsim first_id_byte=0x20 second_id_byte=0xa2 third_id_byte=0x00 fourth_id_byte=0x15 - 64MiB, 2048 bytes page; modprobe nandsim first_id_byte=0xec second_id_byte=0xa1 third_id_byte=0x00 fourth_id_byte=0x15 - 128MiB, 2048 bytes page; modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=0x15 - 256MiB, 2048 bytes page; modprobe nandsim first_id_byte=0x20 second_id_byte=0xac third_id_byte=0x00 fourth_id_byte=0x15 - 512MiB, 2048 bytes page; modprobe nandsim first_id_byte=0xec second_id_byte=0xd3 third_id_byte=0x51 fourth_id_byte=0x95 - 1GiB, 2048 bytes page;
(2)挂载 UBIFS 文件系统
(a) 加载 UBI 内核模块
sudo modprobe ubi mtd=0
这里将 ubi 加载到了 mtd 的设备 0 上
(b) 分离 MTD 上的设备 0
sudo ubidetach /dev/ubi_ctrl -m 0
(c)格式化 MTD 设备并写入 UBI 镜像文件
sudo sudo ubiformat /dev/mtd0 -s 512 -f ubi.img
(d)UBI设备附加回 MTD 设备 0 上
sudo ubiattach /dev/ubi_ctrl -m 0 -O 512
(e)挂载 UBIFS 到指定目录
sudo mount -t ubifs ubi0 /home/biao/test/ubifs/ubifs_simulator
(f)查看挂载状态
biao@ubuntu:~/test/ubifs$ df -h Filesystem Size Used Avail Use% Mounted on ...... ubi0 927M 4.8M 923M 1% /home/biao/test/ubifs/ubifs_simulator ...... biao@ubuntu:~/test/ubifs$ biao@ubuntu:~/test/ubifs/ubifs_simulator$ ls test1 test2 test3 test4
可以看到制作的 UBIFS 镜像文件已经被挂载到了 ubi0 卷上,挂载目录上的文件也就是我们的测试文件。
(四) UBIFS 镜像文件分析
上面我们制作了两个镜像文件 UBIFS 和 UBI,然后再将 UBI 镜像文件加载到 PC机上的 NAND Flash 模拟器 nandsim 上。要了解 UBIFS 的工作原理,我们有必要对它在 Flash 上的数据结构进行分析。
(1)UBIFS 数据结构
ubifs-media.h 中可以看到 UBIFS 所有数据结构定义,下面这个是通用数据结构,有幻数、crc校验、序列号、长度、节点类型、节点组类型这些信息,其中有效节点有11种。
通用头部结构体定义如下
/** * struct ubifs_ch - common header node. * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC) * @crc: CRC-32 checksum of the node header * @sqnum: sequence number * @len: full node length * @node_type: node type * @group_type: node group type * @padding: reserved for future, zeroes * * Every UBIFS node starts with this common part. If the node has a key, the * key always goes next. */ struct ubifs_ch { __le32 magic; __le32 crc; __le64 sqnum; __le32 len; __u8 node_type; __u8 group_type; __u8 padding[2]; } __packed;
节点类型定义:
enum { UBIFS_INO_NODE, UBIFS_DATA_NODE, UBIFS_DENT_NODE, UBIFS_XENT_NODE, UBIFS_TRUN_NODE, UBIFS_PAD_NODE, UBIFS_SB_NODE, UBIFS_MST_NODE, UBIFS_REF_NODE, UBIFS_IDX_NODE, UBIFS_CS_NODE, UBIFS_ORPH_NODE, UBIFS_NODE_TYPES_CNT, };
(2)UBIFS 节点布局
我们前面通过 mkfs.ubifs 制作生成的 UBIFS 镜像文件,它包含 5 种节点类型,在镜像文件中的布局如下图。
