Cramfs、JFFS2、YAFFS2是3种性能优越,专用于嵌入式系统的文件系统。本文通过对这3种文件系统的设计原理和主要性能进行分析与比较,归纳出各自的选型依据和适用领域。
1 三种文件系统的介绍
(1) Cramfs
Cramfs是Linux的创始人Linus Torvalds开发的一种只读文件系统,采用了zlib压缩,压缩比一般可以达到1∶2,但仍可以做到高效的随机读取。在Linux系统中,通常把不需要经常修改的目录压缩存放,在系统引导时再将压缩文件解开。Cramfs并不需要一次性地把文件系统的所有内容都解压到内存中,而只是在系统需要访问某个位置的数据时,马上计算出该数据在Cramfs中的位置,将其解压缩到内存之中,然后通过对内存的访问来获取需要读取的数据。
(2) JFFS2
JFFS意为“Journaling Flash File System”,该文件系统是瑞典Axis通信公司开发的一种基于Flash存储器的日志文件系统。该公司于1999年在GNU/Linux上发行了第一版JFFS文件系统,后来经过Redhat公司的发展,现在已经发行了第二个版本的JFFS2,其全部代码都是可供研究开发的。它在设计时充分考虑了嵌入式系统中Flash存储器的读写特性,确保在系统掉电时,正在读写的文件不受影响;同时,其存储策略以及抗疲劳性等方面也在第一版的基础上进行了改进。目前,JFFS2广泛应用于嵌入式系统中,尤其是嵌入式μClinux操作系统中。
(3) YAFFS2
YAFFS意为“Yet Another Flash File System”,是目前唯一一个专门为NAND Flash设计的文件系统。它采用了类日志结构,结合NAND Flash的特点,提供了损耗平衡和掉电保护机制,可以有效地避免意外掉电对文件系统一致性和完整性的影响。Aleph One公司于2002年5月发布了第一版YAFFS代码,用户可以很方便地在网站上下载到。第一版的YAFFS只支持每页512字节的NAND Flash。YAFFS2是YAFFS的升级版,即支持每页512字节的NAND Flash存储器,也支持每页2 048字节的NAND Flash存储器。
2 Flash存储器
2.1 Flash设备特点
相对于传统存储介质,Flash存储器具有如下优点:
◆ 可靠性高,采用Flash为存储介质,可以确保100万次以上的可靠写入;
◆ 存储速度快,写入/读取均可接近1 Mb/s;
◆ 使用方便,具有很强的环境适应能力。但是,由于其制造工艺的问题,还存在以下缺点:
◆ 读取和写入以页为单位,擦除以块为单位;
◆ 易出现随机坏块;
◆ 块与块之间使用不均衡会导致寿命问题。
2.2 NOR Flash和NAND Flash
基于NOR技术的Flash存储器是最早出现的Flash存储器,它具有以下特点:
◆程序和数据可存放在同一芯片上,拥有独立的数据总线和地址总线,支持快速随机读取,允许系统直接从Flash中读取代码并执行,而无需先将代码下载至RAM中再执行;
◆ 可以单字节或单字编程,但必须以块为单位进行擦除操作。
由于NOR Flash存储器的擦除和编程速度较慢,而块尺寸又比较大,因此擦除和编程操作所花费的时间很长。目前,NOR Flash存储器大多只用于存储嵌入式系统的启动代码。
NAND Flash存储器可以顺序读取存储单元的内容,由于工艺上的进步,其容量越来越大,单位价格越来越便宜,正逐步成为Flash存储器的主流。NAND Flash存储器具有以下特点:
◆ 以页为单位进行读写操作,以块为单位进行擦除操作;页大小为512字节或2 048字节,每页不仅有数据区,还有几十字节的空闲区。
◆ 数据、地址采用同一总线接口,串行读取。
◆ 芯片尺寸小,引脚少,单位成本低。
◆ 芯片内含有失效块,并且在使用过程中可能会出现随机坏块。
表1 比较了这两种Flash存储器的性能参数。
3 三种嵌入式文件系统主要性能分析与比较
数据安全、运行速度、占用资源、使用范围等是嵌入式文件系统的主要性能指标。下面从这几个方面入手,对这三种嵌入式文件系统进行分析与比较。
3.1 数据安全
当前的嵌入式系统主要使用固态Flash芯片作为存储设备。由于Flash设备自身的特点以及嵌入式系统工作环境的复杂多样性,如何保证数据的安全是嵌入式文件系统首先要考虑的问题。数据安全主要包括数据冗余和掉电保护等部分。
3.1.1 数据冗余
提高数据安全性的最有效措施是增加数据的冗余,而数据的大量冗余又给维护数据一致性带来困难。对此应进行合理取舍,在尽量降低冗余的前提下,确保数据的安全性和可靠性。
Cramfs文件系统是一种只读文件系统,文件系统内容不可更改,设计思想遵循“只存储最少的信息”;甚至没有时间戳之类的信息,除了数据包的CRC校验信息外,几乎没有别的数据冗余。
JFFS2文件系统是典型的日志结构的文件系统,它存储的数据是日志式数据信息。JFFS2在Flash上只有两种类型的数据实体: jffs2_raw_inode和jffs2_raw_dirent。前者包含文件的管理信息,后者用于描述文件在文件系统中的位置。真正的数据信息就保持在jffs2_raw_inode节点的后面,大部分管理的信息都是在系统挂载之后建立起来的。两种数据实体有着公共的文件头结构jffs2_unknown_node。在这个结构里,有个jint32_t类型的hdr_crc变量,它代表文件头部中其他域的CRC校验值。这说明JFFS2文件系统使用的是CRC循环冗余校验码。
YAFFS2文件系统是一种类日志文件系统,专用于NAND型Flash设备。其存储数据的基本单位是chunk,相当于Flash的页。Chunk中的数据包括两部分: 一部分是数据区,占用Flash的一页;另一部分是文件信息及冗余数据区,占用Flash页的OOB区。其冗余数据主要是ECC校验数据,对于小页(每页512字节)的Flash,每页有6字节的ECC数据;对于大页(每页2 048字节)的Flash,每页有24字节的ECC数据。
3.1.2 掉电保护
掉电保护的目的是: 在系统意外失去供电的情况下,保证系统运行状态的确定性以及记录数据的完整性;当系统供电恢复后,现场数据可以及时恢复,避免系统产生混乱。很多文件系统出于运行速度考虑,在程序运行过程中,常将数据暂存在SDRAM中;一旦系统意外掉电,往往会造成数据丢失。
Cramfs文件系统将文件系统内容解压到内存中,由于其不能写入,文件系统的内容无法更改,因此不存在掉电保护的问题。
JFFS2是一种日志结构文件系统,因此不论电源以何种方式在哪个时刻停止供电,JFFS2都能保持数据完整性。当系统遭受不正常断电后重新启动时,JFFS2自动将系统恢复到断电前最后一个稳定状态。需要注意的是,文件系统在最后一个稳定状态之后发生的任何改变,都无法进行恢复。
YAFFS2是一种类日志文件系统,可以在意外掉电重启后自动提供可靠的数据记录,防止文件系统的崩溃。它使用独立的日志文件跟踪文件系统内容的变化。举例来说:当应用程序需要写Flash的某一页时,它首先修改的是存放于文件日志中的一块镜像;只有当日志中的镜像复制到文件系统中后,数据才真实地写到该页上。当发生意外掉电重启后,YAFFS2没有像JFFS2那样,使用旧文件完全代替新写文件,而是选择用新文件完全代替旧的文件,或者已写部分使用新文件,未写部分使用旧文件。这种方式增强了掉电时未完全写入文件的安全性能,特别是当意外掉电发生在数据区时,意外掉电时的文件几乎被完好地保存下来。
3.2 运行速度
运行速度可以从以下几个方面考察:文件系统挂载速度,读文件速度,写文件速度等。这里默认的存储介质是NAND Flash。
一般而言,压缩的、只读的文件系统在启动时需要将文件系统解压到SDRAM中,这在一定程度上会减缓文件系统挂载速度。但是,Cramfs文件系统在设计时充分考虑了系统挂载的时间,并没有一次性地把文件系统的所有内容都解压到内存中,而只是在系统需要访问某个位置的数据时,迅速计算出该数据在Cramfs中的位置,将其解压缩到内存之中,再进行访问操作。由于其读文件速度只是找出文件地址以及访问内存的操作,所以无论是挂载速度,还是读文件速度,Cramfs文件系统都比JFFS2和YAFFS2文件系统要快。
JFFS2文件系统的挂载可以分为以下几个步骤进行:
① 检查每个节点CRC校验码的合法性,在内存中为每个节点分配必需的相关的结构;
② 扫描每个i节点的物理节点链表,建立链接;
③ 释放扫描过程中使用的临时信息。可以看出这个过程还是很复杂的,更重要的是,在JFFS2文件系统被挂载时,需要对整个Flash存储区域进行扫描,这就耗费了大量的时间,因此JFFS2文件系统的开机速度非常缓慢。与JFFS2相比,YAFFS2减少了一些功能,挂载时只需扫描Flash存储器的空闲区,根据从OOB备份数据区)中读取的yaffs_tags信息判读是文件头页面还是数据页面,再根据相应信息在内存中为每个文件建立一个对应的yaffs_object对象。由于YAFFS2在系统加载时只需扫描各个页面的OOB区,即可建立起整个文件系统的结构,而不需要像JFFS2那样扫描整个Flash设备,因此大大加快了文件系统的启动速度。但是,YAFFS2仍然要求对整个Flash设备所有页的空闲进行扫描,这就导致当Flash设备存储空间变大时,系统扫描时间会直线上升。
在文件系统运行时,JFFS2需要维护几个链表来管理擦写块。根据擦写块上的内容,一个擦写块可能会在不同的链表上。具体来说,当一个擦写块上都是合法(valid)的节点时,它会在clean_list上;当一个擦写块包含至少一个过时(obsolete)的节点时,它会在dirty_list上;当一个擦写块被擦写完毕,并被写入Cleanmarker节点后,它会在free_list上。正是以这些链表为基础,JFFS2文件系统才可以实现垃圾回收、损耗平衡等性能。文件系统挂载时YAFFS2在内存中建立一个层次结构的索引,可以看作是一个文件树。树的最底层指向实际存放文件的页面,高层则是一层层的索引目录。文件树是通过一个联合结构的节点来实现的。联合结构是固定大小(32字节),当它处于最底层时,由16个2字节入口来查找页面ID;当处于其它层时,由8个4字节指针指向其他更低层的节点。每个文件对应一个文件树,便于检索。在以NAND Flash为存储介质的嵌入式系统中,YAFFS2的文件读写性能都优于JFFS2。
3.3 占用资源
在嵌入式系统中,内存和外存资源匮乏,需要节约使用。因此占用的内存和外存资源量也是衡量嵌入式文件系统的一个重要指标。
如果使用RAMDISK方式运行文件系统,那么在系统运行之后,首先要把Flash上的映像文件解压到内存中,才可以开始运行程序。这样,同样的代码不仅在外存中占据了空间,而且由于解压缩,还在内存中占用了更大的空间。Cramfs虽然是一个压缩式的文件系统,但前面已介绍过,它并不需要一次性地将所有内容解压到内存之中。由于其对文件内容的压缩,因此无论是外存还是内存,Cramfs都比JFFS2和YAFFS2占用更少的资源。
JFFS2将文件系统的数据和原数据以节点的形式存储在Flash上,其冗余数据只是节点头部的CRC校验码,并且在存储时对节点的数据进行了压缩。与之相比,YAFFS2不仅存储了页数据的ECC校验码,并且没有用Flash设备OOB区存储数据,而是用来存储页节点信息。其映像也是由若干个页面组成,没有对数据进行压缩,因此YAFFS2文件系统映像大大超过了JFFS2文件系统映像。实验表明,普通YAFFS2映像的大小约为同样JFFS2映像大小的2倍。当文件系统在内存中运行时,JFFS2在内存中定义了若干链表,YAFFS2在内存中为每个文件创建一个文件树。尽管为每个文件建立一个文件树需要消耗掉不少的内存,但是比起维护JFFS2所需的链表消耗的内存,还是少一些。在实际运行中,YAFFS2占用的内存要小于JFFS2文件系统。
3.4 使用范围
要使用Cramfs或JFFS2文件系统,离不开MTD驱动程序层的支持。MTD(Memory Technology Device)是Linux中的一个存储设备通用接口层,虽然也可以建立在RAM上,但它是专为基于Flash的设备而设计的。MTD包含特定Flash芯片的驱动程序,Flash芯片驱动向上层提供读、写、擦除等基本的Flash操作方法。MTD对这些操作进行封装后向用户层提供MTD char和MTD block类型的设备。MTD char类型的设备包括/dev/mtd0等,可以对Flash的原始字符访问;MTD block类型的设备包括/dev/mtdblock0等,将Flash模拟成块设备,这样就可以在这些块设备上创建Cramfs或JFFS2等格式的文件系统。在对用户层的接口上,Cramfs或JFFS2文件系统还需要操作系统VFS(虚拟文件系统)的支持。
同样,YAFFS2文件系统支持使用MTD驱动层和VFS层,这也是一般在嵌入式Linux或者μClinux操作系统中使用YAFFS2文件系统的常用方法。除此之外,YAFFS2还带有NAND Flash芯片驱动,并为嵌入式系统提供了直接访问文件系统的API,用户可以不使用Linux中的MTD和VFS,直接对文件进行操作。图1为嵌入式系统的文件系统结构。
YAFFS2中的YAFFS2 Direct提供了直接的文件系统接口,因此在那些没有VFS层的嵌入式系统中也可使用YAFFS2。YAFFS2文件系统的使用范围要比Cramfs和JFFS2文件系统广泛;但是需要注意的是,YAFFS2只能用在NAND Flash存储设备上。
图1 三种文件系统结构
4 结论
这3种嵌入式文件系统在嵌入式系统中的应用非常广泛,但是又具有各自的特点。
具体分析如下:
① Cramfs是最早的一种嵌入式文件系统,它只存储了最少的信息,对文件内容进行了压缩,运行速度比较快。但是由于其不能写入,不支持超过16 MB大小的文件,因此广泛地应用于存储空间小、文件系统内容较少并且不需要用户写入的嵌入式系统中。
② JFFS2是一种较早的Flash专用文件系统。目前已有很多JFFS2应用于μClinux以及嵌入式Linux的例子。事实上,它已经成为新版Linux中的一种标准文件系统。同时它对NOR Flash和NAND Flash提供支持,针对两种Flash设备共同的特性,提供掉电保护和损耗平衡等功能,可供用户读写,十分适合于同时用到这两种Flash设备的嵌入式系统。
③ YAFFS2是专为NAND Flash设计的文件系统,它充分考虑了NAND Flash设备的特性,支持ECC校验,提供掉电保护和损耗平衡功能,运行和挂载速度都比JFFS2要快。它支持操作系统广泛,包括常见的嵌入式Linux、WinCE、μClinux。由于它提供Driect接口,因此稍加修改也可使用在没有操作系统的嵌入式系统中。目前,被广泛应用于使用NAND Flash作为存储设备的嵌入式系统中。
表2 3种文件系统性能分析与对比
针对具体应用,可以在考察各种文件系统特点的基础上,选择更适合自身系统的一种文件系统解决方案。比如在许多以NAND Flash为存储介质的应用系统中,都用到“Cramfs+YAFFS2”文件系统。以Cramfs作为根文件系统,既提高了启动速度,也能保护根文件不受破坏;使用YAFFS2作为用户文件系统,在保证用户可以自由读写文件的基础上,提高了文件系统的安全性和运行速度,不失为一种优良的嵌入式文件系统解决方案。