jff2概述
JFFS2全称jouranlling Flash File System Version2,即日志文件系统,是Redhat公司开发的开源闪存文件系统,其前身是JFFS, 最早只支持NOR Flash, 自2.6版以后开始支持NAND Flash, 在闪存上使用非常广泛,同时在嵌入式系统中被普遍的应用。JFFS2直接在闪存上提供文件系统,而不是模拟块设备。有关更多信息,请参阅JFFS2 PDF。
jffs2出现的原因
(1)闪存的特性和限制
- 闪存的最小寻址单元是字节,而不是磁盘的扇区(sector)
- 闪存只能从1写成0,这就意味着每次写之前,要先擦除(将0变成1)
- 由于硬件设计的差异,nor读写的基本单位是字节,nand一般按块擦除,按页读写
(2)闪存转换层
将磁盘文件系统(ext2, FAT)运行在闪存上的很自然的方法就是在文件系统和闪存之间提供一个闪存转换层(Flash Translation Layer), 它的功能就是将底层的闪存模拟成一个具有 512字节扇区大小的标准块设备(block device)。对于文件系统来说,就像工作在一个普通的块设备上一样,没有任何的差别
(3)jffs2的特性
1.jffs2是一个日志型文件系统,包含数据和元数据(meta-data)的节点在闪存上顺序存储
JFFS2擦除块大小
与最初的JFFS不同,JFFS2单独处理闪存的每个擦除块。它永远不会写入从一个擦除块交叉到另一个擦除区块的节点,并且如果遇到具有此类节点的文件系统,它将无法处理。任何跨越擦除块边界的节点都将被JFFS2忽略,并且它包含的数据将丢失,从而导致文件系统损坏。
如果在当前擦除块的末尾没有足够的空间来写入整个节点,JFFS2将使其为空,并继续写入新的擦除块。
此外,JFFS2总是在擦除块的开头开始写入,并且不希望在在中间找到空闲空间。擦除块中的任何可用空间都应该一直到擦除块的末尾。JFFS2将打印warning,例如:
jffs2_scan_empty(): Empty block at 0x0012fffc ends at 0x00130000 (with 0xe0021985)! Marking dirty
如果它在擦除块中的节点之间找到空闲空间。不过,这种情况是无害的——只是浪费了一点空间,仅此而已。
如果使用错误的擦除块大小值(-e选项)使用mkfs.jffs2创建JFFS2镜像,则可能会出现这两种情况。默认值是64KiB,因为这是你可能经常遇到的最小擦除块大小,而创建一个擦除块大小比实际硬件小的镜像是无害的——它只会发出令人讨厌的消息。如果您看到上面提到的消息,请检查设备的擦除块大小(如果您不知道,请查看/proc/mtd),并使用正确的-e选项为其创建JFFS2映像。
Erase Block Summary (EBS)
EBS的目标是加快装载过程。它在每个擦除块的末尾存储摘要信息。在装载时,不再需要单独扫描所有节点(并读取擦除块的所有页面),足以读取此“小”摘要。
此摘要信息存储在JFFS2_FEATURE_RWCOMPAT_DELETE节点中。在装载过程中,如果擦除块的末尾没有摘要节点,则将执行原始扫描过程。
如果为写入数据启用EBS,则会自动生成此节点,但您也应该使用名为sumtool的用户空间工具,在使用mkfs.JFFS2创建JFFS2映像后插入摘要信息。例子:
mkfs.jffs2 -rdir -oout.jffs2 -e128KiB
sumtool -iout.jffs2 -oout-sum.jffs2 -e128KiB
EBS也应该与NOR和NAND一起工作。在NAND芯片和擦除块大小较大的芯片上,加速速率通常较高。
jffs2数据存储
(1)节点头部定义
jffs2将文件系统的数据和原数据以节点的形式存储在闪存上,节点的头部信息如下
- 幻数屏蔽位:0x1985 用来标识 JFFS2 文件系统
- 节点类型:jffs2自身定义了三种节点类型,但考虑到文件系统可扩展和兼容性,jffs2从ext2借鉴经验,节点类型的最高两位被用来定义节点的兼容性属性,兼容性属性:
- JFFS2_FEATURE_INCOMPAT:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_INCOMPAT,那么 JFFS2 必须拒绝挂载(mount)文件系统。
- JFFS2_FEATURE_ROCOMPAT:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_ROCOMPAT,那么 JFFS2 必须以只读的方式挂载文件系统。
- JFFS2_FEATURE_RWCOMPAT_DELETE:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_RWCOMPAT_DELETE,那么在垃圾回收的时候,这个节点可以被删除。
- JFFS2_FEATURE_RWCOMPAT_COPY:当 JFFS2 发现了一个不能识别的节点类型,并且它的兼容属性是 JFFS2_FEATURE_RWCOMPAT_COPY,那么在垃圾回收的时候,这个节点要被拷贝到新的位置。
- 节点总长度:包括节点头和数据的长度
- 节点头部CRC校验:包含节点头部的校验码
(2)节点类型
1.JFFS2_NODETYPE_INODE
INODE节点包含了i节 点的原始数据(i节点号,文件的组ID,属主ID,访问时间,偏移和长度等),文件数据放在INODE节点后面,每个INODE节点还有一个版本号,用来维护属于一个i节点的所有INODE节点的全序关系。
2.JFFS2_NODETYPE_DIRENT
DIRENT 节点就是把文件名与 i 节点对应起来。在 DIRENT节点中也有一个版本号,这个版本号的作用主要是用来删除一个 dentry。具体来说,当我们要从一个目录中删除一个 dentry 时,我们要写一个 DIRENT 节点,节点中的文件名与被删除的 dentry 中的文件名相同,i 节点号置为 0,同时设置一个更高的版本号。
3.JFFS2_NODETYPE_CLEANMARKER
当一个擦写块被擦写完毕后,CLEANMARKER 节点会被写在 NOR flash 的开头,或 NAND flash 的 OOB(Out-Of-Band) 区域来表明这是一个干净,可写的擦写块。在 JFFS v1 中,如果扫描到开头的 1K 都是 0xFF 就认为这个擦写块是干净的。但是在实际的测试中发现,如果在擦写的过程中突然掉电,擦写块上也可能会有大块连续 0xFF,但是这并不表明这个擦写块是干净的。于是我们需要 CLEANMARKER 节点来确切的标识一个干净的擦写块
(3)节点在内存中的表示和操作
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-节点的物理节点链表的下一个节点。
(4)jfffs2挂载过程
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 释放在扫描过程中使用的临时信息。
(5)jffs2垃圾回收机制
当 free_list 上的擦写块数太少了,垃圾回收就会被触发。垃圾回收主要的任务就是回收那些已经过时的节点,但是除此之外它还要考虑磨损平衡的问题。因为如果一味的从 dirty_list上选取擦写块进行垃圾回收,那么 dirty_list 上的擦写块将先于 clean_list 上的擦写块被磨损坏。JFFS2 的处理方式是以 99% 的概率从 dirty_list,1% 的概率从 clean_list 上取一个擦写块下来。由此可以看出 JFFS2 的设计思想是偏向于性能,同时兼顾磨损平衡
jffs2优缺点
优点
- 有效地利用闪存空间:JFFS2的特点是将闪存分解成块,并在逐块使用当中实现闪存空间管理,有效地减少了闪存空间的浪费。
- 良好的可靠性:JFFS2是一个日志文件系统,在封装数据之前会有一个完整的日志记录,保证了数据的完整性。在写入数据时,JFFS2首先将数据写入内存中的缓冲区中,然后在一定条件下再将数据从缓冲区写入闪存中,这样可以保证数据的正确性,避免了在写入过程中出现的错误。
- 可以在不同平台上使用:JFFS2可以运行在不同的平台上,并与各种芯片和配置相兼容,这使得它更加方便在不同的嵌入式设备上使用。
缺点
- 性能较低:由于JFFS2的日志记录和缓冲区写入机制,其性能明显低于一些其他的嵌入式文件系统,如UBIFS、YAFFS2等。
- 可靠性问题:在一些极端的情况下,如电源中断或者设备重置等,JFFS2的日志记录也可能会出现不完整的情况,导致数据的损失和文件系统的崩溃。
- 不支持压缩:JFFS2不支持数据压缩,这在一些嵌入式设备中会受到限制,因为这些设备的闪存容量很有限。
- 挂载时间长,挂载过程需要从头扫描到结尾
- 磨损平衡的随意性:jffs2对磨损均衡用概率的方法解决的
- jffs2会占用内存:占用内存的大小同i节点数和闪存上的节点数成正比的,因此实际应用在128Mflash以下的应用中
源码数据结构
可以参考内核的jffs2.h头文件
实例分析
相关文章
- JFFS2文件系统(1)
- 📎jffs2.pdf