wear leveling的功能是通过block alloctor实现的。littlefs通过两个方法实现磨损均衡:
1) 检测和恢复bad blocks
2)通过dynamic blocks均衡磨损
检测和恢复bad blocks和block alloctor的关系并不紧密,主要依靠文件系统本身来检测坏块并标记。在littlefs中,检测坏块主要是通过写后回读并比较的方式实现的,如果数据不一致则说明是一个坏块。一旦检测到了坏块就要进行恢复。
如果是写入错误,因为数据才RAM中还有备份,因此需要block allctor分配一个新的block,再次写入。新块替换坏块是通过CObW(copy-on-bounded-writes)数据结构实现的。CObW的一个属性就是任何一个block可以在COW的过程中替换。bounded-writes通常是由写入次数计数器触发的,检测到坏块后同样可以处处一个COW操作。
如果是读错误,则要复杂一些。因为在读取时,RAM中没有数据的备份,因此需要一个方法能恢复原来的数据。ECC就是方法之一。但是,littlefs本身并不提供ECC。littlefs采取的方法是在所有的block尽量分配磨损,同时希望在所有的block达到使用寿命前不会有某个block坏掉。
wear leveling有两种策略:
1)动态wear leveling,通过动态block来分配磨损,仅考虑未使用的block;
2)静态wear leveling,同时在动态block和静态block上分配磨损,需要考虑所有的block,包括已有存储了数据的block。通常可以认为静态block就是已经写入了数据的block。
综合考虑代码大小和复杂度,littlefs采取动态wear leveling。这是一种尽最大努力的解决方法。这种方法中,磨损不是非常好的分配,只是在空闲块中分配,但是也大大的延长了flash的使用寿命。
在littlefs中,使用了基于统计信息的wear leveling算法。即不是追踪和记录每个block的磨损情况,而是依赖所有block的磨损的均匀分布uniform distribution来近似动态wear leveling算法。block alloctor实现了均匀分布uniform distribution,主要包括两部分:
1)第一次上电创建文件系统时,block alloctor对所有的block线性分配
2)文件系统创建后,再次上电时,不能再从头开始线性分配了,而是在mount文件系统时block alloctor从一个随机偏移的block开始后续分配,只要这个随机值是均匀的,那么block的分配也会是近似均匀的。
从上图中可以看出,随着时间的延长,各个block的磨损逐渐趋于一致。
在实际的使用过程中,每次上电后的随机数的会根据flash上已有的数据来计算和产生。主要是对metadata pair的checksum进行xor计算得到。利用metadata pair的一个原因也是因为在mount的时候会获取metadata pair的数据。
上述的随机数生成器并不完美,它只有在文件系统发生了更改之后才会产生一个不同的数。但这真是文件系统的block alloctor和磨损均衡所需要的。
总得来说坏块检测和磨损均衡都是尽最大努力的方法,来避免flash block的快速磨损。