基本概念
littlefs中设计了两种类型的链表,一个是ctz链表,把单个文件占用的多个物理block串起来;另外一个是目录链表,将所有目录占用的物理block串起来。
block pair
block pair是特殊的一对block,互为主备的身份,用于存储元数据,并且只会存储一个目录的信息。Block pair的数据更新逻辑如下所示:
rev表示当前block数据的新旧程度,rev的值越大,数据越新。当block1 存储满了之后,会对数据进行多余内容的剔除(压缩),然后全部提交到block2中。
tag
对文件系统的任何修改,都会通过tag来标识本次操作的属性,该tag会提交到对应的block pair中。
Tag的标准结构如下图所示:
其中,valid标识tag是否有效,type是操作的类型(创建、删除、更新等),id是文件的唯一id号,length是额外数据的长度,data就是额外的数据,例如:当创建文件的时候,需要额外的信息来说明文件的名字。
特别说明:valid为0说明是有效的,并且tag是大端模式存储的,这样valid就直接在地址的最低端存在,顺序访问flash的时候,能快速判断tag是否有效。
一个block中的第一个tag需要与0xffffffff异或,然后写入flash,第二个tag需要和第一个tag异或,再写入flash,这样设计的目的,是为了以最小的代价,达成block能够前向遍历和后向遍历的目的。
后向遍历的主要作为是为了快速读取文件的信息,因为tag是最新的才是有效值,而最新的往往在block的后面位置,通过后向遍历,能减少遍历的时间,加快访问。
ctz list
下图所示,为传统的COW类型文件系统增加block的过程。
图 传统的方式添加数据
传统的方式添加数据,需要建立旧的数据到新的数据的索引。这样做有两个弊端:
(1)当数据特别多的时候,这样一个个的索引过去查找,比较费时间。
(2)当使用cow机制的时候,在文件后面每增加一次数据,需要把所有的索引都重新建立一个,这样带来的性能损耗特别大。
为了解决这两个问题,littlefs设计了ctz list,其利用ctz的特性和反向链接的思想,为不同的块之间添加了多个链接,ctz list添加数据的方式如下图所示:
图 ctz list添加数据
具体的优化为:
- 采用逆向的指针,这样常规的追加数据,不需要额外的开销来重新建立所有的索引;
- 每个偶数block有多个指针,指向更远的数据,这样可以在检索的时候加快速度。
指针的建立采用了CTZ(二进制中最后一个不为0的位,其右边0的个数)的方式。大概原理就是,block N 如果是一个能被 2^X整除的数,那么他就存在指向N – 2^X的指针,指针的数目为ctz(N)+1。
Block N |
0 |
1 |
2 |