本文思惟导图总纲:
html
关于ubi子系统,早已有比较正式的介绍,也提供很是形象的介绍ubi子系统ppt
国内的前辈 alloysystem 不辞辛劳为咱们提供了部分正式介绍的中文译文,以及找不到原文的转载译文linux
感谢这些资料让我迅速入门ubi,进而整理出这博文算法
此博文是对上文的总结以及中文译文的补充ide
上图很是形象地描述了从Flash到UBIFS的各个层次。从上图咱们发现,MTD子系统在实际的Flash驱动之上 ,而UBI子系统则在MTD子系统之上。工具
要对比UBI和MTD的概念,咱们不妨问本身一个问题,UBI和MTD两个不一样的层次的"使命"分别是什么?学习
Flash驱动直接操做设备,而MTD在Flash驱动之上,向上呈现统一的操做接口。因此MTD的"使命"是 屏蔽不一样Flash的操做差别,向上提供统一的操做接口 。ui
UBI基于MTD,那么UBI的目的是什么呢? 在MTD上实现nand特性的管理逻辑,向上屏蔽nand的特性 。.net
nand有什么特性呢?
(下文描述的 Nand驱动,是广义上的操做Nand的集合,包括fs/ubi/mtd的层次,而非纯粹的nand驱动)unix
1. 操做最小单元为页(Page)/块(Block) Nand不一样于Nor,Nor能够以字节为单位操做Flash,但Nand的读写最小单元是页,擦除最小单元是块。 对常见的1Gbit的spinand而言,其页大小2KBytes,块大小是128K,表示一个块有64个页。 2. 擦除寿命限制 Nand的物理性质决定了其每一个块都有擦除寿命的限制,SLC约10W次,MLC约5000次,TLC约1000次。 所以,Nand驱动必需要作到磨损平衡。 所谓磨损平衡,就是尽量均衡使用每个块,既不让一个块太大压力,也不让一个块太过空闲。 3. 位翻转(bit-flips) Nand的物理性质使其可能会在使用、保存过程当中出现位翻转的现象。 例如,原始数据为0xFFFC,在存储过程当中Flash的数据却变成了0xFFFF。 因此要不在nand内部,要不在nand控制器都会存在ecc校订模块,在位翻转后校订。 然而,ecc并非万能的,其校订能力有限,因此驱动必须在位翻转数量进一步变多以前把数据搬移到其余块。 萌新可能会有疑问,ecc都已经校订了为何还要搬移?由于ecc校订的是从Flash中读到内存中的数据, 而不是Flash自己存储的数据,换句话说,此时Flash中的数据依然是错的,若是不搬移,随着翻转的位数量积累, ecc就校订不了了,此时就至关于永久丢失正确数据了。 4. 存在坏块(Bad Block) 制做工艺和Nand自己的物理性质,致使在出厂和正常使用过程当中都会产生坏块。 所谓坏块,就是说这个块已经损坏,不能再用于存储数据,所以Nand驱动须要能自动跳过坏块。
关于SLC/MLC/TLC的比较,可参考这篇博客
若是说UBI在MTD之上,在FS之下的中间层,用于抽象MTD屏蔽nand差别,那么ubifs就是正儿八经的文件系统。
ubifs是基于UBI子系统的文件系统,实现文件系统该有的全部基本功能,例如文件的实现,例如日志的实现。
这里须要特别注意的是,ubifs跟jffs/yaffs相比,并不包含nand特性的管理,而是交由ubi来实现。
Block Layer是适用于常见块设备的通用块层,其特有的概念有bio、request、电梯算法等,其典型的设备有磁盘、SSD、mmc等。
而ubi基于mtd,虽然能模拟块设备,从本质上来说其并非块设备。跟踪UBIFS的IO操做,发现其IO操做并不通过通用块设备层。
FTL(Flash Translation Layer)是一个"黑盒子",其跟UBI很是像,都是对nand特性进行封装。
按个人理解,UBI跟FTL的目标不一样,致使其实现上会有差别。UBI屏蔽nand特性是为了对接UBIFS,而FTL则是为了对接Block Layer。例如MMC其实也是封装起来的Nand,只不过在MMC内部实现了FTL,通过FTL的转换就能以块设备层的方法直接操做Nand,就能在mmc上格式化常见的块文件系统,例如EXT、VFAT等。
在UBI中还有两个概念,分别是UBI卷(UBI Volume)和UBI设备(UBI Device)。这两个概念,咱们能够这么理解:
UBI设备 至关于 磁盘设备(sda,mmcblk0) UBI卷 至关于 磁盘上对应分区(sda1,mmcblk0p1)
换句话说,UBI设备是在MTD设备上建立出来的设备,而UBI卷则是从UBI设备上划分出来的分区, 从设备节点名(ubi0)和卷名(ubi0_3)能够看出端倪。
上面的描述是为了方便理解UBI卷和UBI设备,实际上UBI卷和分区的概念之间仍是有差异的。
在UBI子系统中,还有LEB和PEB的概念:
LEB指Logical Erase Block,即逻辑擦除块,简称逻辑块,表示逻辑卷中的一个块 PEB指Physical Erase Block,即物理擦除块,简称物理块,表示物理Nand中的一个块
为何要划分逻辑块和物理块?从PPT中咱们能够发现,物理块和逻辑块存在动态映射关系,且因为UBI头的存在,逻辑块通常会比物理块小2个页。
UBI子系统就是ubifs与mtd之间的中间层,其向下链接MTD设备,实现nand特性的管理逻辑,向上呈现无坏块的卷。
因此UBI子系统的做用,主要包括两点:
1. 屏蔽nand特性(坏块管理、磨损平衡、位翻转) 2. UBI卷的实现
UBI卷的逻辑擦除块(LEB)与物理擦除块(PEB)之间是动态映射的,详细能够看PPT
ubi的工具集成在包mtd-utils中,分别有如下工具及其做用
工具 | 做用 |
---|---|
ubinfo | 提供ubi设备和卷的信息 |
ubiattach | 连接MTD设备到UBI而且建立相应的UBI设备 |
ubidetach | ubiattach相反的操做,将MTD设备从UBI设备上去连接 |
ubimkvol | 从UBI设备上建立UBI卷 |
ubirmvol | 从UBI设备上删除UBI卷 |
ubiblock | 管理UBI卷上的block |
ubiupdatevol | 更新卷,例如OTA直接更新某个分区镜像 |
ubicrc32 | 使用与ubi相同的基数计算文件的crc32 |
ubinize | 制做UBI镜像 |
ubiformat | 格式化空的Flash设备,擦除Flash,保存擦除计数,写入UBI镜像到Flash |
mtdinfo | 报告从系统中找到的UBI设备的信息 |
UBI子系统须要往每一个物理块的开头写入两个关键数据,这两个关键数据就叫作UBI的头部。
这两个数据分别是 此物理块擦除次数头 和 此物理块的逻辑卷标记头,也分别称为 EC头(Erase Count) 和 VID头(Volume IDentifier)。
不论是EC头仍是VID头,都是64Bytes,分别记录与Nand块的第一个页和第二个页。
以Q&A的形式介绍UBI头:
Q:为何要这两个头? A:前文有说道,nand每一个block有擦除寿命限制,所以须要记录擦除次数,以实现磨损平衡,所以须要EC头。此外,为了实现卷,必须记录卷的逻辑块与物理块之间的映射关系,所以须要VID头。 Q:为何不合并成1个头? A:二者写入的时机不一致,致使两个头必须分开写入。EC头在每次擦除后,必须立刻写入以免丢失,而VID头只有在映射卷后才会写入。 Q:不论是EC头仍是VID头都是64B,为何要用2个Page? A:使用2个Page是对Nand来讲的。前文有说过,Nor的读写最小单元是Byte,而Nand的读写最小单元是Page,所以对Nor能够只使用64Bytes,对Nand则必须使用2个Page,就是说,即便只有64Bytes有效数据,也须要用无效数据填充满1个Page一次性写入。 Q:在记录擦除次数时掉电等,致使丢失实际擦除次数怎么办? A:取全部物理块的擦除次数的平均数
关于UBI头部的详细介绍,可参考连接
UBI子系统有个对用户隐藏的特殊卷,叫层卷(layout volume),用来记录卷表。咱们能够把卷表等价于分区表,记录各个卷的信息。卷表大小为2个逻辑擦除块,每一个逻辑擦除块记录一份卷表,换句话说,UBI子系统为了保证卷表的可靠性,用2个逻辑记录2分卷标信息。
因为层卷的大小是固定的(2个逻辑块),致使能保存的卷信息受限,因此最大支持的卷数量是随着逻辑块的大小改变而改变的,但最多不超过128个。
卷表中每一个卷都保存了什么信息?
struct ubi_vtbl_record { __be32 reserved_pebs; //物理块数量 __be32 alignment; //卷对齐 __be32 data_pad; __u8 vol_type; //静态卷or动态卷标识 __u8 upd_marker; //更新标识 __be16 name_len; //卷名长度 __u8 name[UBI_VOL_NAME_MAX+1]; //卷名 __u8 flags; //经常使用语自动重分配大小标记 __u8 padding[23]; //保留区域 __be32 crc; //卷信息的CRC32校验值 } __packed;
由这个结构体咱们能够发现,卷信息是被CRC32保护着的。比较有意思的有两个成员:vol_type 和 flags
vol_type成员标记了卷的类型,在建立卷时指定,可选动态卷和静态卷。那么什么是动态卷?什么又是静态卷?
动态卷和静态卷是两种卷的类型,静态卷标记此卷只读,因而UBI子系统使用CRC32来校验保护整个卷的数据,动态卷是可读写的卷,数据的完整性由文件系统来保证。
关于静态卷和动态卷的介绍,可参考连接
flags成员经常使用于标识是否自动重分配大小。怎么样自动充分配大小呢?在首次运行时自动resize卷,让卷大小覆盖全部未使用的逻辑块。
例如Flash大小是128M,在烧录的镜像中分配的全部卷加起来只用了100M,若是有卷被表示为autoresize
,那么在首次运行时,那个卷会自动扩大,把剩余的28M囊括在内。
这个功能挺实用的,例如某个方案规划中,除去rootfs、内核等必要空间外,把剩余全部空间尽量分配给用户数据分区。
在开发过程当中加了个应用,致使rootfs卷须要更大的空间,进而须要压缩user_data卷的空间。
若是user_data空间是autoresize的,那么user_data卷的空间就会自动压缩。
再例如旧方案用的是128M的nand,后面升级为256M,即便使用相同的固件,也不用担忧多出来的128M浪费掉了,
由于user_data卷自动扩大囊括多出来的128M。
须要注意的是,只容许1个卷设置autoresize标志
关于更新标识更多的介绍,参考连接
咱们知道Nand的物理性质,致使在使用久以后会产生坏块,那么UBI是如何判断好块是否变成了坏块的呢?
有两个场景可能会标识坏块,分别是写失败和擦除失败。擦除失败且返回是EIO,则直接标记坏块。比较有意思的是写失败的判断逻辑。
UBI子系统有后台进程对疑似的坏块进行"严刑拷打"(torturing),有5个步骤:
1. 擦除嫌疑坏块 2. 读取擦除后的值,判断是否都是0xFF(擦除后理应全为0xFF) 3. 写入特定数据 4. 读取并校验写入的数据 5. 以不一样的数据模式重复步骤1-4
若是"严刑拷打"出问题,则标记坏块,详细的实现逻辑可参考函数torture_peb()
原文可参考连接
什么是管理开销呢?为了管理Nand的空间,实现磨损平衡、坏块管理等等功能,必须占用一部分空间来存储关键数据,就好像文件系统的元数据。管理占用的空间是不会呈现给用户空间使用的,这空间即为管理的开销。
对Nand来讲,UBI管理开销主要包含5个部分:
1. 层卷(卷表) : 占用两个物理块 2. 磨损平衡:占用一个物理块 3. 逻辑块修改原子操做:占用一个物理块 4. 坏块管理:默认每1024个块则预留20个块(内核参数可配:CONFIG_MTD_UBI_BEB_LIMIT) 5. UBI头:(物理块总数*2)个页
坏块管理预留的块数量,也能够理解为最大能容纳多少个坏块;再考虑坏块的存在,管理开销计算公式为:
UBI管理总开销 = 特性开销 + UBI头开销 其中: 坏块预留 = MAX(坏块数量,坏块管理预留数量) 特性开销 = (坏块预留 + 1个磨损平衡开销 + 1个原子操做开销 + 2个层卷开销) * 物理块大小 UBI头开销 = 2 * 页大小 * (含坏块的总块数 - 坏块预留 - 1个磨损平衡开销 + 1个原子操做开销 + 2个层卷开销) 也就是说: UBI管理总开销 = (坏块预留 + 4) * 物理块大小 + 2 * 页大小 * (含坏块的总块数 - 坏块预留 - 4)
以128M的江波龙的FS35ND01G-S1F1 SPI Nand为例,其规格为:
总大小:128M(1Gbit) 页大小:2K bytes 块大小:128K 块数量:1024
假设是彻底无坏块的片子,其管理开销为:
UBI管理开销 = (20 + 4) * 128K + 2 * 2K * (1024 - 20 - 4) = 7072K ≈ 7M
详细参考原文连接