1.文件索引树结构

大多数现代文件系统都喜欢使用 B-trees 或类似的结构来管理索引(index)以定位文件中的 blocks。大多数文件系统中通过使用“extents”来减少文件数据块的总索引大小。F2FS 不采用 B-tree 结构管理索引,也不使用extents减少文件数据块索引的大小(虽然F2FS为每个节点(node)维护一个extent作为提示)

所谓的extent指的是一段连续的物理磁盘块,这样,只需要一个extent数据结构我们就能描述一段很长的物理磁盘空间,性价比很高。ext4文件系统中的extent数据结构的主要作用是索引,即根据逻辑块号查询文件的extent能够准确定位逻辑块对应的物理块号,另外,ext4的extent在文件较小的时候存储在inode的i_data[]中,在文件较大的时候,所有的extent会被组织成一棵B+树

2.索引节点

管理数据位置的关键数据结构是“node”,与传统文件系统相似,F2FS 使用三种 node:inode,直接 node,间接 node。F2FS 分配4 KB 的空间给一个 inode,其中包括923个数据块索引指针,两个一级索引块(直接 node )指针,两个二级索引块(间接 node )指针,以及一个三级索引块(二级间接 node)指针。一个一级索引块(直接 node )包含1018个数据块指针,一个二级索引块(间接 node )包含1018个一级索引块(直接 node )指针,一个三级索引块(二级间接 node)包含1018个二级索引块(间接 node )指针。因此一个文件的最大大小是:

4 KB * (923 + 21018 + 210181018 + 10181018*1018) := 3.93 TB

3.文件数据块索引


文件系统一个重要功能是实现文件的保存以及读写。每个文件系统有不同的保存和读写方式,而F2FS则是通过Node-Data的形式将数据组织起来。其中最重要的结构就是f2fs_node结构,它实现了文件数据的组织和管理。

f2fs_node的结构以及作用
F2FS每一个文件,根据文件大小的不同,对应了一个或者多个f2fs_node结构,它实际存在于磁盘当中,并每一个f2fs_node结构都占据了4KB的磁盘空间,它的结构如下:

struct f2fs_node {union {struct f2fs_inode i;struct direct_node dn;struct indirect_node in;};struct node_footer footer; // footer用于记录node的类型
} __packed;struct node_footer {__le32 nid;      /* node id */__le32 ino;        /* inode nunmber */__le32 flag;     /* include cold/fsync/dentry marks and offset */__le64 cp_ver;      /* checkpoint version */__le32 next_blkaddr;    /* next node page block address */
} __packed;
/*其中f2fs_inode、direct_node、indirect_inode、node_footer 在 f2fs_node 中起到不同的作用:f2fs_inode: 每一个文件仅存在一个f2fs_inode结构,用于提供给文件进行索引,保存了部分文件的元数据,以及923个页数据的物理地址,同时也保存了5个用于间接寻址的direct_node或indirect_node的node id(nid),其中2个是direct_node的nid,2个是indirect_node的nid,以及1个double indirect node的nid;
direct_node: 用于直接寻址,保存了1018个页数据的物理地址;
indirect_inode: 用于间接寻址,保存了1018个direct_node的nid或者1018个indirect_inode的nid;
node_footer: 用于区分该f2fs_node的类型f2fs_inode和direct_inode以及indirect_inode的结构如下所示:
*/
struct f2fs_inode {...// DEF_ADDRS_PER_INODE=923,因此f2fs_inode直接用一个数组保存了物理地址的值__le32 i_addr[DEF_ADDRS_PER_INODE];// DEF_NIDS_PER_INODE=5,因此同样是用一个数组直接存放了用于间接寻址的direct_node和indirect_node的nid__le32 i_nid[DEF_NIDS_PER_INODE];    ...
} __packed;struct direct_node {__le32 addr[ADDRS_PER_BLOCK]; // ADDRS_PER_BLOCK=1018,保存数据的物理地址
} __packed;struct indirect_node {__le32 nid[NIDS_PER_BLOCK]; // NIDS_PER_BLOCK=1018,保存了用于间接寻址的nid
} __packed;

普通文件数据的保存

从上节描述可以知道,一个文件由一个f2fs_inode和多个direct_inode或者indirect_inode所组成。当系统创建一个文件的时候,它会首先创建一个f2fs_inode写入到磁盘,然后用户往该文件写入第一个page的时候,会将数据写入到main area的一个block中,然后将该block的物理地址赋值到f2fs_inode->i_addr[0]中,这样就完成了Node-Data的管理关系。随着对同一文件写入的数据的增多,会逐渐使用到其他类型的node去保存文件的数据。

根据f2fs_inode能够保存的数据的变量f2fs_inode->i_addr以及f2fs_inode->i_nid,可以计算F2FS最大允许单个文件的大小:

    a). f2fs_inode 直接保存了923个页的数据的物理地址b). f2fs_inode->i_nid[0~1] 保存了两个 direct_node 的地址,因此可以通过二级索引的方式访问数据,因此这里可以保存 2 x 1018个页的数据c).  f2fs_inode->i_nid[2~3] 保存了两个indirect_node 的地址,这两个其中2个indirect_node保存的是 direct_node 的nid,因此可以通过二级索引的方式访问数据,因此可以保存 2 x 1018 x 1018个页的数据;d). f2fs_inode->i_nid[4] 保存了一个indirect_node 的地址,这个indirect_node保存的是 indirect_node 的nid,因此可以通过三级索引的方式访问数据,因此可以保存 1018 x 1018 x 1018个页的数据

可以得到如下计算公式: 4KB x (923 + 2 x 1018 + 2 x 1018 x 1018 + 1 x 1018 x 1018 x 1018) = 3.93TB 因此F2FS单个文件最多了保存3.93TB数据。

内联文件数据的保存

从上节可以知道,文件的实际数据是保存在f2fs_inode->i_addr对应的物理块当中,因此即使一个很小的文件,如1个字节的小文件,也需要一个node和data block才能实现正常的保存和读写,也就是需要8KB的磁盘空间去保存一个尺寸为1字节的小文件。而且f2fs_inode->i_addr[923]里面除了f2fs_inode->i_addr[0]保存了一个物理地址,其余的922个i_addr都被闲置,造成了空间的浪费。

因此F2FS为了减少空间的使用量,使用内联(inline)文件减少这些空间的浪费。它的核心思想是当文件足够小的时候,使用f2fs_inode->i_addr数组直接保存数据本身,而不单独写入一个block中,再进行寻址。因此,如上面的例子,只有1个字节大小的文件,只需要一个f2fs_inode结构,即4KB,就可以同时将node信息和data信息同时保存,减少了一半的空间使用量。

根据上述定义,可以计算得到多大的文件可以使用内联的方式进行保存,f2fs_inode有尺寸为923的用于保存数据物理地址的数组i_addr,它的数据类型是__le32,即4个字节。保留一个数组成员另做它用,因此内联文件最大尺寸为: 922 * 4 = 3688字节。

目录
LFS(Log-structured File System) 实际上对目录布局没有强加任何特殊的要求,因而访问 F2FS 文件系统的目录等同于访问其他文件系统的目录。目录的主要目的是提供更快速的文件名查找,为每个可使用 telldir() 报告的文件名提供稳定的地址。

原始的 Unix 文件系统支持256字节的文件名长度,使用与 Ext2 相同的目录机制——在布满目录项的文件中顺序遍历查找文件名。这种方法简单有效,但是对于大目录的扩展性不好。

多数现在主流文件系统如 Ext3,XFS 和 Btrfs 使用多种机制(包括 B-tree),有时通过哈希索引文件名。B-tree 的一个问题是节点(nodes)需要切分,这导致一些目录项在文件中频繁移动,这给 telldir() 提供文件名对应的稳定地址带来了额外的挑战。这大概就是 telldir() 经常被认为是不太好的接口的主要原因。

4.1 目录结构

一个目录项占据11 Bytes,也就是包含以下属性:

(1)    – hash     文件名的哈希值
(2)    – ino       inode 号
(3)    – len       文件名长度
(4)    – type     文件类型,如目录,符号链接,等等

一个目录项 block 包含214个目录项槽位(slot)和文件名,其中用一个 bitmap 表示每个目录项是否有效,1个目录项块占用 4 KB,结构如下:

Dentry block(4 Kb) = bitmap(27bytes) + Reserved(3bytes) + Dentries(11214bytes) + filename(8214bytes)

4.2 目录哈希

F2FS 使用一些连续搜索和哈希方法提供一个简单的搜索机制,合理高效,简单实现提供稳定的 telldir() 的地址。F2FS 文件系统的大多数的哈希算法代码都是参考 Ext3,但是删除(忽略)了使用每个目录作为seed。F2FS 的 seed 使用一个秘密的随机数,以确保满足使用的哈希值在每个目录中都不同,因而是不可预测的。使用这样的 seed 可以给哈希冲突提供保护。虽然哈希冲突在现实中是不可避免的,但使用这种方式可以很容易避免这种冲突,这种删除(忽略) 使用每个目录作为seed 带来的效果很意外。

很容易想到目录结构是一系列的哈希表连续地存储在一个文件中,每个哈希表具有一组相当大的 buckets。查找过程从第一个哈希表到下一个哈希表,在每一个阶段对合适的bucket 执行线性查找,直到找到文件名或者查找到最后的那个哈希表。在查找过程中,在合适的 bucket 中的任意空闲空间都会被记录,以防需要创建文件名的时候用到。

F2FS 对目录结构实现了多级哈希表,每一级都有一个使用专用数字的哈希 bucket 的哈希表,如下所示。注意,“A(2B)”表示一个哈希 bucket 包含两个数据块。

----------------------
A : bucket    哈希bucket
B : block     数据块
N : MAX_DIR_HASH_DEPTH   最大目录哈希层级
----------------------
level #0:   A(2B)
level #1:   A(2B) - A(2B)
level #2:   A(2B) - A(2B) - A(2B) - A(2B).       .       .       .       .
level #N/2:  A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B).        .       .       .       .
level #N:   A(4B) - A(4B) - A(4B) - A(4B) - A(4B) - ... - A(4B)

所有的哈希表按如下方式组织:第一个哈希表恰好有1个 bucket,大小是两个数据块,因而对于最开始的少数几百个目录项,使用简单的线性搜索。第二个哈希表有2个 bucket,第三个哈希表有4个 bucket,第四个哈希表有8个 bucket,然后是16 个 bucket,……,直到第31个哈希表有大约10亿(2^30)个 bucket,每个 bucket 都是两个数据块大小。


F2FS 在目录中查找文件名时,首先计算文件名的哈希值,然后在 level #0 中扫描哈希值查找包含文件名和文件的 inode 的目录项。如果没有找到,F2FS在 level #1 中扫描下一个哈希表,用这种方式,F2FS 以递增的方式扫描每个 level 的哈希表(如果上一 level 中没有查找到结果),在每个 level中,F2FS 仅需要扫描一个bucket,而该 bucket 的号是由文件名的哈希值与该 level 中的 buckets 个数的相除取余得到的。也就是每个 level 中需要扫描的一个 bucket 由下式确定的,查找复杂度是 0:

bucket number to scan in level #n = (hash value) % (# of buckets in level #)

所以哈希表的最终结果就是需要线性搜索几百个目录项,如果目录项很大的话,需要通过几个数据块的搜索。搜索长度仅随着目录文件中目录项的个数对数级增长,所以容易扩展。这种搜索当然比完全的线性搜索算法要好,但是看起来好像比实际需要做的工作更多。但是它保证了每次加入或删除一个文件名的时候仅需要更新一个数据块,因为目录项没有移动,目录项在文件中的偏移对于 telldir() 来说就是一个稳定的地址,这是非常有意义的特性。

在创建文件的情况下,F2FS 在哈希表的buckets 中找到该创建文件名对应的空的连续的槽位(slots)(这一句的原文是:F2FS finds empty consecutive slots that cover the file name)。F2FS 自哈希表的 level #0 到level #N 从哈希表中给搜索该空闲的槽位,与查找文件系统中已有文件的查找操作一样。

F2FS 基础知识二相关推荐

  1. CV:计算机视觉技术之图像基础知识(二)—图像内核的可视化解释

    CV:计算机视觉技术之图像基础知识(二)-图像内核的可视化解释 目录 图像内核的可视化解释 测试九种卷积核 官方Demo DIY图片测试 DIY实时视频测试 相关文章 CV:计算机视觉技术之图像基础知 ...

  2. CV:计算机视觉技术之图像基础知识(二)—以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础)

    CV:计算机视觉技术之图像基础知识(二)-以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础) 目录 ...

  3. (五)JS基础知识二(通过图理解原型和原型链)【三座大山之一,必考!!!】

    JS基础知识二(原型和原型链) 提问 class 继承 类型判断(instanceof) 原型 原型关系 基于原型的执行规则 原型链 说明 提问 如何准确判断一个变量是不是数组 class的原型本质 ...

  4. oracle:oracle基础知识(二)

    oracle基础知识(二)笔记:高级查询 文章目录 分组查询 多属性分组语法: 过滤查询 group by 语句增强 sqlplus报表功能 多表查询 等值连接 外连接 自连接 子查询 子查询中的空值 ...

  5. 网络基础知识(二) HTTP

    网络基础知识(二) HTTP 黑发不知勤学早,白首方悔读书迟. 内容参考:https://www.runoob.com/http/http-content-type.html HTTP协议是Hyper ...

  6. CV:计算机视觉技术之图像基础知识(二)—图像内核的九种卷积核可视化解释(blur/bottom sobel /emboss/identity /sobel /outline/sharpen)

    CV:计算机视觉技术之图像基础知识(二)-图像内核的九种卷积核可视化解释(blur/bottom sobel /emboss/identity /left sobel /outline/right s ...

  7. scikit-learn学习基础知识二

    scikit-learn学习基础知识二 文章目录 scikit-learn学习基础知识二 一.介绍 二.代码实现 三.运行结果 四.总结 一.介绍 本文我们学习scikit-learn中的KNeigh ...

  8. kettle对字符串去除空格_整理|ABAP基础知识二:常用字符串处理

    常用字符串处理 上一期整理了ABAP的数据类型和定义,今天我们整理一下开发过程中常见的字符串处理命令.虽然说这些基础知识对于已经熟练使用Ctrl c + Ctrl v的资深码农来说过于简单.但是对于一 ...

  9. [Python学习] 专题五.列表基础知识 二维list排序、获取下标和处理txt文本实例

    通常测试人员或公司实习人员需要处理一些txt文本内容,而此时使用Python是比较方便的语言.它不光在爬取网上资料上方便,还在NLP自然语言处理方面拥有独到的优势.这篇文章主要简单的介绍使用Pytho ...

最新文章

  1. java的标记接口_Java中的标记接口?
  2. DB2 XQuery学习笔记
  3. Cisco WLAN 控制器的配置
  4. 原子操作之sync/atomic
  5. win10 远程问题汇总
  6. 浅析Java内存模型--ClassLoader
  7. Spring8:一些常用的Spring Bean扩展接口
  8. macbook服务器文件,使用MacBook生成服务器使用的p12证书文件
  9. 如何修改PKG_CONFIG_PATH环境变量
  10. Rinne Loves Xor
  11. linux 第三章红帽子,红帽子 Linux_命令全解
  12. golang 安全的tcp server_Go 语言使用 TCP_NODELAY 控制发包流量
  13. 锤子新机终于来了?10月31日发布,连海报都做好了?
  14. Python使用reduce()函数计算多个集合的并集与交集
  15. 关于Android的学习
  16. CCPC-Wannafly Winter Camp Day3 (Div2, onsite) I 石头剪刀布(按秩合并并查集)
  17. DMA驱动开发(6,参考资料)有用链接
  18. 使用Kotlin语言两年后,我有话要说
  19. 团队如何限制合适的在制品(WIP)数量
  20. 常用的十种算法:二分查找,分治,动态规划,KMP

热门文章

  1. 现要求输入一个整数n,请输出斐波那契数列的第n项
  2. Android安全测试神器大全
  3. python 手机自动化交易股票_通达信转python,机智股票自动交易手机版
  4. 云计算,经济危机下爆发
  5. 【CodeForces 1255D --- Feeding Chicken】
  6. 企业中台最佳实践--阿里数据中台最佳实践(九)
  7. 爬虫 - 收藏集 - 掘金
  8. php英文怎么读,100的英文怎么读_单词及读音
  9. 【NLP基础理论】03 文本分类
  10. CSIRO Detects Raw Materials Used in the Ma IoT PLC accessking of the First Stars