UBIFS文件系统(一)
作者:互联网
UBIFS文件系统(一)
本文主要介绍ubifs的基本概念,后续会描述ubifs各个关键过程的实现细节。
磁盘结构
ubifs文件系统将整个磁盘空间划分为superblock、master、log、lpt、orphan和main六个区域,其区域划分如下所图所示:
其中,superblock区域固定占用LEB0,master区域固定占用LEB1和LEB2,其他区域占据的LEB数量则视该文件系统分区实际占有的总的LEB数量而定,orphan区域一般占用1到2个LEB。
superblock区域保存文件系统的固定参数,参数在格式化时写入,除了leb_cnt元素和UBIFS_FLG_SPACE_FIXUP标志位会在首次挂载时被改变,其他元素皆为只读。
master区域中的两个LEB相互备份,以确保在异常掉电的情况能够恢复master区域的内容。master区域的数据在每次发生commit的时候进行更新。
log区域记录日志数据的存储位置lnum:offs。ubifs是一种日志文件系统,文件数据采用异地更新的方式(out_of_place_update),即文件数据的更新不会每次都同步到flash,而是记录到日志区,当日志区的数据累计到一定程度时,才将数据同步到flash中。
lpt区域记录了磁盘空间中各个LEB的状态(free、dirty、flag),用于实现对LEB的分配、回收和状态查询。
main区域则保存文件的数据和索引。
文件索引
ubifs文件系统中的数据统一以node的形式顺序存储于LEB中,其中node包含metadata和data两部分:metadata标识了node包含的数据类型,包括ubifs_ino_node、ubifs_data_node、ubifs_dent_node、ubifs_xent_node和ubifs_trun_node五种;data部分则为实际的有效数据。node的长度根据数据类型的不同而有所差别,node在磁盘中的存储序列如下所示:
ubifs采用一棵B+树对文件的数据进行索引:其中保存文件路径信息的node组成B+树的索引节点,组成文件数据的node组成B+树的叶子节点。B+树中的索引节点在内存中为ubifs_znode,该结构体只有部分数据需要保存到flash中,而其他部分只存在于内存当中,保存到flash中的部分组成结构体为ubifs_idx_node。ubifs将用于索引文件数据的B+树称之为TNC(Tree Node Cache)树,其索引结构如下所:
假设B+树的数高为N,每个索引节点包含的最大分支数为M,则该B+数能够索引到的最大叶子节点数为M的(N+1)次方个。在ubifs的TNC中,B+树的树高N默认为bottom_up_buf=64(该值可以根据实际情况进行扩展);每个索引节点包含的最大分支数M为8(该值在进行文件系统分区格式化时指定),而每个索引节点实际占有的分支数记录与索引节点的child_cnt域中。
TNC中Level 1至Level N的内部索引节点(ubifs_znode)的分支(ubifs_zbranch)分别指向下一级索引节点的存储位置(lnum:offs:len),而Level 0处的索引节点的分支则指向数据节点的存储位置(lnum:offs:len)。保存索引节点和数据节点的存储位置,而不保存节点的实际数据内容,可以有利用在同一个LEB中保存多的节点,减少LEB的访问次数,提高访问效率。B+树通过键值进行索引,数据节点的键值计算规则如下:
其中,对于同属于一个文件node的多个数据节点ubifs_data_node,其键值key按照block number进行区分;对于同属于一个文件node的多个目录项节点ubifs_dent_node,其键值key以目录项文件名的hash值进行区分;同理,ubifs_xent_node以attr entry的hash值进行区分。注意:在进行键值比较时,首先比较键值的低32位,再比较两者的高32位,因此同属于同一个inode的所有数据node都保存在B+树的相邻位置,这样便可以快速的找到同一个inode的各个数据node。在进行crash分析时,可能需要用到利用键值查找znode/zbranch的过程。此外,TNC的叶子节在内存中集合被称之为LNC(Leaf Node Cache),便于查找需要频繁访问的目录项ubifs_dent_node以及扩展属性项(噶和目录项共用一个数据结构)。
索引节点(index-node)和叶子节点(非索引节点 non-index-node)永远保存在不同的LEB中,即同一个LEB不可能同时包含index node和non-index node。
空间管理
ubifs采用另一个B+树对磁盘的空间进行管理,此时B+树的叶子节点保存的是LEB属性(空闲空间free、脏空间dirty和是否是索引LEB),该B+树在ubifs中被称之为LPT(LEB Props Tree),其索引结构如下图所示:
LPT和TNC两者的区别在于:TNC中叶子节点和索引节点采用统一的数据结构ubifs_znode表示,其保存的内容一致均为key、lunm、offs和len,而在LPT中其索引节点对应的数据结构为ubifs_nnode,保存索引节点所在的lnum和offs,其叶子节点对应的数据结构为ubifs_pnode,保存LEB的空间属性free、dirty和flag;TNC各索引节点的分支数不确定,但在LPT中每个索引节点包含固定的分支数fanout=4;对于固定大小的文件系统分区,TNC的树高不确定,其树高由文件系统实际包含的文件数(数据节点的数量)决定,而LPT的树高固定为log4M,M为main区域的LEB数量。TNC中需要根据不同的数据节点计算键值,并作为节点的一个元素伴随节点一起插入到B+树中,而LPT中不需要额外的键值用于索引节点,而是直接以LEB号作为键值对节点进行索引,LPT叶子节点从左到右其LEB号依次为LEB(main first) ~ LEB(main first + M)。
LPT存在big model和small model两种存储模式,当所有的nnode、pnode、ltab、lsave能够存储到一个LEB中时,使用small model,否则使用big model。两种模式的区别在于回写磁盘时的规则不同,对于small model其在flash中的数据存储格式如下所示:
LPT为main区域LEB的空间管理索引树,而ltab则为LPT区域的空间管理索引树,标记LPT区域的LEB的使用情况。
日志管理
ubifs是一种应用于nand flash之上的文件系统,对nand flash进行写操作之前必须以earse block为单位对其进行擦除,并且擦除之后只能对其写一次。在这种情况下,对文件数据先读、再擦、再写的inplace_update方式就变得非常耗时,因此ubifs采用out_of_place_update(即异地更新)的方式对文件数据进行操作。异地更新是指将修改的文件数据写到已经擦除过的磁盘块,并且在内存中修改文件索引将其指向新的数据块,此时原始的文件索引关系仍保存在磁盘,而新的文件索引存储在内存当中,存储在内存中的索引关系会根据情况定时的同步到磁盘中(在ubifs中称之为commit),此时才完成真正的文件数据修改。
ubifs将所有写入新数据的磁盘块称之为日志区(journal),它并不是一段连续的LEB块区域,而是由任意位置和任意多个main区域的LEB组成。根据不同的数据类型,ubifs包含三个动态变化的日志区,分别是GC、BASE和DATA。ubifs以LEB为单位对flash进行写操作,因此每一个日志区在内存中都维护一个称之为journal head的缓冲区,其大小与LEB相同,当ubifs写数据时总是先将数据写到对应的缓冲区中,当缓冲区填满之后才将数据实际写到磁盘当中,这也是为了减少对磁盘的操作次数。
ubifs将每个作为日志区的LEB的信息(lnum:offs)以ubifs_ref_node的数据结构记录于log区域,为区分log区域中的数据是否同步到了flash,ubifs在每次同步操作时向log区域写入一个ubifs_cs_node以作标识。ubifs中的所有node结构体都包含一个统一的头部元素为ubifs_ch,在元素中包含有一个标记该node创建时间的squem,因此log区域中大于ubifs_cs_node的squem的ubifs_ref_node即为尚未被同步到flash的日志LEB,反之则为已经被同步到flash的日志LEB,其对应的ubifs_ref_node则可以被删除以腾空间。当系统发生异常掉电时,ubifs扫描log区域的LEB,便可对掉电前的TNC和LPT等数据结构进行重建,从而达到掉电恢复的目的。
在数据进行同步(即commit)时,ubifs_cs_node总是寻找一个新的LEB,并占据其起始位置,每当一个LEB加入到日志区时,ubifs便会创建一个ubifs_ref_node结构体,并将该结构体同步到磁盘中。log区的数据排列结构如下所示:
ubifs_cs_node和ubifs_ref_node的数据结构分别如下所示:
ubifs在内存中还维护了一个与ubifs_ref_node对应的bud结构便于快速查找日志区的LEB。
标签:node,UBIFS,ubifs,LEB,文件系统,索引,数据,节点 来源: https://blog.csdn.net/qq_35018427/article/details/117968237