DIY编程器网

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1227|回复: 0
打印 上一主题 下一主题

[待整理] JFFS2 文件系统及新特性介绍

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-11 01:57:12 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
JFFS2 是一个开放源码的项目(www.infradead.org)。 它是在闪存上使用非常广泛的读/写文件系统,在嵌入式系统中被普遍的应用。这篇文章首先分析了在闪存上使用 JFFS2 的必要性,然后详细的阐述了 JFFS2 实现的内部机制,包括日志结构的文件系统,关键的数据结构,挂载过程和垃圾收集机制。同时也指出了 JFFS2 的局限性,并介绍了最新的针对 JFFS2 的不足进行改进的补丁程序。最后对 JFFS3 的设计思想和现在的开发状况给予了简单的介绍。
1. 为什么需要 JFFS2</strong>
这一小节首先介绍了闪存相对于磁盘介质的特别之处,然后分析了将磁盘文件系统运行在闪存上的不足,同时也给出了我们使用 JFFS2 的理由。

1.1 闪存(Flash Memory) 的特性和限制</strong>
这里所介绍的闪存的特性和限制都是从上层的文件系统的角度来看的,而不会涉及到具体的物理特性。总的来说,有两种类型的 flash memory: NOR flash 和 NAND flash. 先介绍一下这两种闪存所具有的共同特性。
A) 闪存的最小寻址单位是字节(byte),而不是磁盘上的扇区(sector)。这意味着我们可以从一块闪存的任意偏移(offset)读数据,但并不表明对闪存写操作也是以字节为单位进行的。我们会在下面的阐述中找到答案。
B) 当一块闪存处在干净的状态时(被擦写过,但是还没有写操作发生),在这块flash上的每一位(bit)都是逻辑1。
C) 闪存上的每一位(bit)可以被写操作置成逻辑0。 可是把逻辑 0 置成逻辑 1 却不能按位(bit)来操作,而只能按擦写块(erase block)为单位进行擦写操作。擦写块的大小从 4K 到128K 不等。从上层来看,擦写所完成的功能就是把擦写块内的每一位都重设置(reset)成逻辑 1。
D) 闪存的使用寿命是有限的。具体来说,闪存的使用寿命是由擦写块的最大可擦写次数来决定的。超过了最大可擦写次数,这个擦写块就成为坏块(bad block)了。因此为了避免某个擦写块被过度擦写,以至于它先于其他的擦写块达到最大可擦写次数,我们应该在尽量小的影响性能的前提下,使擦写操作均匀的分布在每个擦写块上。这个过程叫做磨损平衡(wear leveling)。
NOR flash 与 NAND flash 的不同之处:
A) NOR flash 读/写操作的基本单位是字节;而 NAND flash 又把擦写块分成页(page), 页是写操作的基本单位,一般一个页的大小是 512 或 2K 个字节。对于一个页的重复写操作次数是有限制的,不同厂商生产的 NAND flash 有不同的限制,有些是一次,有些是四次,六次或十次。
B) 按照现在的技术水平,一般来说NOR flash擦写块的最大可擦写次数在十万次左右,NAND flash擦写块的最大可擦写次数在百万次左右。

1.2 闪存转换层</strong>
将磁盘文件系统(ext2, FAT)运行在闪存上的很自然的方法就是在文件系统和闪存之间提供一个闪存转换层(Flash Translation Layer), 它的功能就是将底层的闪存模拟成一个具有 512字节扇区大小的标准块设备(block device)。对于文件系统来说,就像工作在一个普通的块设备上一样,没有任何的差别。

2. JFFS2</strong>
有 JFFS2 就要有 JFFS v1,没错,JFFS v1 最初是由瑞典的 Axis Communications AB 公司开发的,使用在他们的嵌入式设备中,并且在 1999 年末基于 GNU GPL 发布出来。最初的发布版本基于 Linux 内核 2.0,后来 RedHat 将它移植到 Linux 内核 2.2,做了大量的测试和 bug fix 的工作使它稳定下来,并且对签约客户提供商业支持。但是在使用的过程中,JFFS v1 设计中的局限被不断的暴露出来。于是在 2001 年初的时候,RedHat 决定实现一个新的闪存文件系统,这就是现在的 JFFS2。下面将详细介绍 JFFS2 设计中主要的思想,关键的数据结构和垃圾收集机制。这将为我们实现一个闪存上的文件系统提供很好的启示。首先,JFFS2 是一个日志结构(log-structured)的文件系统,包含数据和原数据(meta-data)的节点在闪存上顺序的存储。JFFS2 之所以选择日志结构的存储方式,是因为对闪存的更新应该是 out-of-place 的更新方式,而不是对磁盘的 in-place 的更新方式。在闪存上 in-place 更新方式的问题我们已经在闪存转换层一节描述过了。

2.1 节点头部定义和兼容性</strong>
JFFS2 将文件系统的数据和原数据以节点的形式存储在闪存上,具体来说节点头部的定义如下:

2.2 节点类型</strong>
JFFS2 定义了三种节点类型:
JFFS2_NODETYPE_INODE: INODE 节点包含了i-节点的原数据(i节点号,文件的组 ID, 属主 id, 访问时间,偏移,长度等),文件数据被附在 INODE 节点之后。除此之外,每个 INODE 节点还有一个版本号,它被用来维护属于一个i-节点的所有 INODE 节点的全序关系。下面举例来说明这个全序关系在 JFFS2 的使用:

2.3 JFFS2节点,擦写块在内存中的表示和操作</strong>
JFFS2 维护了几个链表来管理擦写块,根据擦写块上的内容,一个擦写块会在不同的链表上。具体来说,当一个擦写块上都是合法(valid)的节点时,它会在 clean_list 上;当一个擦写块包含至少一个过时(obsolete)的节点时,它会在 dirty_list 上;当一个擦写块被擦写完毕,并被写入 CLEANMARKER 节点后,它会在 free_list 上。
通常情况下,JFFS2 顺序的在擦写块上写入不同的节点,直到一个擦写块被写满。此时 JFFS2 从 free_list 上取下一个擦写块,继续从擦写块的开头开始写入节点。当 free_list 上擦写块的数量逐渐减少到一个预先设定的阀值的时候,垃圾回收就被触发了,为文件系统清理出更多的可用擦写块。为了减少对内存的占用,JFFS2 并没有把 i 节点所有的信息都保留在内存中,而只是把那些在请求到来时不能很快获得的信息保留在内存中。具体来说,对于在闪存上的每个 i 节点,在内存里都有一个 struct jffs2_inode_cache 与之对应,这个结构里保存了 i 节点号,指向 i 节点的连接数,以及一个指向属于这个 i 节点的物理节点链表的指针。所有的 struct jffs2_inode_cache 存储在一个哈希表中。闪存上的每个节点在内存中由一个 struct jffs2_raw_node_ref 表示,这个结构里保存了此节点的物理偏移,总长度,以及两个指向 struct jffs2_raw_node_ref 的指针。一个指针指向此节点在物理擦写块上的下一个节点,另一个指针指向属于同一个 i-节点的物理节点链表的下一个节点。

2.4 JFFS2 挂载过程</strong>
JFFS2 的挂载过程分为四个阶段:
1) JFFS2 扫描闪存介质,检查每个节点 CRC 校验码的合法性,同时分配了 struct jffs2_inode_cache 和 struct jffs2_raw_node_ref
2) 扫描每个 i 节点的物理节点链表,标识出过时的物理节点;对每一个合法的 dentry 节点,将相应的 jffs2_inode_cache 中的 nlink 加一。
3 找出 nlink 为 0 的 jffs2_inode_cache,释放相应的节点。
4 释放在扫描过程中使用的临时信息。

2.5 JFFS2 垃圾回收机制</strong>
当 free_list 上的擦写块数太少了,垃圾回收就会被触发。垃圾回收主要的任务就是回收那些已经过时的节点,但是除此之外它还要考虑磨损平衡的问题。因为如果一味的从 dirty_list上选取擦写块进行垃圾回收,那么 dirty_list 上的擦写块将先于 clean_list 上的擦写块被磨损坏。JFFS2 的处理方式是以 99% 的概率从 dirty_list,1% 的概率从 clean_list 上取一个擦写块下来。由此可以看出 JFFS2 的设计思想是偏向于性能,同时兼顾磨损平衡。对这个块上每一个没有过时的节点执行相同的操作:
1 找出这个节点所属的 i 节点号(见图五)。
2 调用 iget(),建立这个 i 节点的文件映射表。
3 找出这个节点上没有过时的数据内容,并且如果合法的数据太少,JFFS2 还会合并相邻的节点。
4 将数据读入倒缓存里,然后将它拷贝到新的擦写块上。
5 将回收的节点置为过时。
当擦写块上所有的节点都被置为过时,就可以擦写这个擦写块,回收使用它。

3. JFFS2 的不足之处
3.1 挂载时间过长</strong>
JFFS2 的挂载过程需要对闪存从头到尾的扫描,这个过程是很慢的,我们在测试中发现,挂载一个 16M 的闪存有时需要半分钟以上的时间。
3.2 磨损平衡的随意性(random nature)</strong>
JFFS2 对磨损平衡是用概率的方法来解决的,这很难保证磨损平衡的确定性。在某些情况下,可能造成对擦写块不必要的擦写操作;在某些情况下,又会引起对磨损平衡调整的不及时。
3.3 很差的扩展性</strong>
JFFS2 中有两个地方的处理是 O(N) 的,这使得它的扩展性很差。
首先,挂载时间同闪存的大小,闪存上节点数目成正比。
其次,虽然 JFFS2 尽可能的减少内存的占用,但通过上面对 JFFS2 的介绍我们可以知道实际上它对内存的占用量是同 i 节点数和闪存上的节点数成正比的。
因此在实际应用中,JFFS2 最大能用在 128M 的闪存上。

4. JFFS2 的新特性</strong>
最近加入到 JFFS2 中的两个补丁程序分别解决了上面提到的挂载时间过长和磨损平衡随意性的问题。
4.1 磨损块小结补丁程序(erase block summary patch)</strong>
这个补丁程序最基本的思想就是用空间来换时间。具体来说,就是将每个擦写块每个节点的原数据信息写在这个擦写块的最后,当 JFFS2 挂载的时候,对每个擦写块只需要读一次来读取这个小结节点,因此大大减少了挂载时间。使用了磨损块小结补丁程序,一个擦写块的结构就像下面这样:

4.2 改进的磨损平衡补丁程序</strong>
这个补丁程序的基本思想是,记录每个擦写块的擦写次数,当闪存上各个擦写块的擦写次数的差距超过某个预定的阀值,开始进行磨损平衡的调整。调整的策略是,在垃圾回收时将擦写次数小的擦写块上的数据迁移到擦写次数大的擦写块上。这样一来我们提高了磨损平衡的确定性,我们可以知道什么时候开始磨损平衡的调整,也可以知道选取哪些擦写块进行磨损平衡的调整。
4.3 擦写块头部补丁程序</strong>
在写改进的磨损平衡补丁程序的过程之中,我们需要记录每个擦写块的擦写次数,这个信息需要记录在各自的擦写块上。可是我们发现 JFFS2 中缺少一种灵活的对每个擦写块的信息进行扩展的机制。于是我们为每个擦写块引入了擦写块头部(header),这个头部负责纪录每个擦写块的信息(比如说擦写次数),并且它提供了灵活的扩展机制,将来如果有新的信息需要记录,可以很容易的加入到头部之中。

5. JFFS3 简介</strong>
虽然不断有新的补丁程序来提高 JFFS2 的性能,但是不可扩展性是它最大的问题,但是这是它自身设计的先天缺陷,是没有办法靠后天来弥补的。因此我们需要一个全新的文件系统,而 JFFS3 就是这样的一个文件系统,JFFS3 的设计目标是支持大容量闪存(>1TB)的文件系统。JFFS3 与 JFFS2 在设计上根本的区别在于,JFFS3 将索引信息存放在闪存上,而 JFFS2将索引信息保存在内存中。比如说,由给定的文件内的偏移定位到存储介质上的物理偏移地址所需的信息,查找某个目录下所有的目录项所需的信息都是索引信息的一种。 JFFS3 现在还处于设计阶段,文件系统的基本结构借鉴了 Reiser4 的设计思想,整个文件系统就是一个 B+ 树。JFFS3 的发起者正工作于垃圾回收机制的设计,这是 JFFS3 中最复杂,也是最富有挑战性的部分。JFFS3 的设计文档可以在http://www.linux-mtd.infradead.org/doc/jffs3.html 得到,有兴趣的读者可以积极参与到 JFFS3 的设计中,发表自己的见解,参与讨论。


致谢</strong>
在这里要特别感谢David Woodhouse, Artem B. Bityutskiy,Joern 和 Thomas Gleixner,在参与到JFFS2的开发过程中,这几位主要的项目维护者(maintainer)不断地给我帮助,耐心的回答我的问题,在与他们的讨论过程中碰撞出很多智慧的火花和富有启发性的思想,尤其是他们对我的补丁程序提出的”尖刻”的问题,让我不断的努力思考(thinking hard),不断的完善它们。我想这种开放,无私,客观的精神正是开源社区的精髓所在,吸引着越来越多的开发者参与进来,并使开源社区不断的壮大。

50bc2e34037ed.gif (861 Bytes, 下载次数: 11)

图一

图一

50bc2e34c4f2d.gif (1.57 KB, 下载次数: 13)

图二

图二

50bc2e358c2e1.gif (4.26 KB, 下载次数: 23)

图三

图三

50bc2e365aea6.gif (3.44 KB, 下载次数: 11)

图四

图四

50bc2e375c8d2.gif (13.3 KB, 下载次数: 13)

图五

图五

50bc2e382f850.gif (1.74 KB, 下载次数: 11)

图六

图六
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|文字版|手机版|DIY编程器网 ( 桂ICP备14005565号-1 )

GMT+8, 2025-1-26 18:27 , 耗时 0.086452 秒, 21 个查询请求 , Gzip 开启.

各位嘉宾言论仅代表个人观点,非属DIY编程器网立场。

桂公网安备 45031202000115号

DIY编程器群(超员):41210778 DIY编程器

DIY编程器群1(满员):3044634 DIY编程器1

diy编程器群2:551025008 diy编程器群2

QQ:28000622;Email:libyoufer@sina.com

本站由桂林市临桂区技兴电子商务经营部独家赞助。旨在技术交流,请自觉遵守国家法律法规,一旦发现将做封号删号处理。

快速回复 返回顶部 返回列表