有 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 的更新方式。
1 JFFS2文件系统简介
ROMFS是滋Clinux默认的根文件系统,它相对于一般的EXT2文件系统具有节约空间等优点,但它却是一种只读文件系统,不支持动态擦写和保存。尽管对于需要动态保存的数据可以采用虚拟ram盘的方法来保存,可是一旦系统掉电,ram盘的内容就会全部丢失。
为了克服上述问题,人们设计开发了JFFS2文件系统。JFFS文件系统是一种基于Flash的日志文件系统。它在设计时充分考虑了Flash的读写特性和嵌入式系统用电池供电的特点,在读取文件时,若遇系统突然掉电,确保其文件的可靠性不受影响。后来,JFFS文件系统又进行了一系列改进,形成了JFFS2文件系统。JFFS2主要改善了存取策略以提高Flash的抗疲劳性,同时也优化了碎片整理性能,增加了数据压缩功能。JFFS2的不足是:当文件系统已满或接近满时,由于垃圾收集方面的原因,会引起JFFS2运行速度显着降低。
JFFS2 是一个开放源码的项目。 它是在闪存上使用非常广泛的读/写文件系统,在嵌入式系统中被普遍的应用。这篇文章首先分析了在闪存上使用 JFFS2 的必要性,然后详细的阐述了 JFFS2 实现的内部机制,包括日志结构的文件系统,关键的数据结构,挂载过程和垃圾收集机制。同时也指出了 JFFS2 的局限性,并介绍了的针对 JFFS2 的不足进行改进的补丁程序。
2 硬件系统
系统采用Samsung的ARM7TDMI芯片S3C44B0X作为微处理器。S3C44B0X上的存储系统地址空间分为8个存储体,每个存储体可达32MB,共计256MB.Bank0~Bank5可支持ROM、SRAM,Bank6和Bank7可支持ROM、SRAM和FP/EDO/SDRAM等。Flash芯片采用的是SST公司的NOR型芯片SST39VF160(1×16MB),将它的片选连接到S3C44B0X的nGCS0引脚,映射到Bank0上,地址范围为0x00000000~0x001fffff.SDRAM选用Samsung的16位芯片K4S641632F,将它与S3C44B0X的GCS6引脚相接,映射到Bank6上,地址范围为0x0c000000~0x0c7fffff[6].
S3C44B0X与其他处理器相比具有一个很重要的特点,即不支持Remap.图1是复位后的S3C44B0X的存储器映射图。一旦Flash和SDRAM的片选与S3C44B0X的nGCSx引脚连接之后,它们在S3C44B0X地址空间中的映射位置就固定不变了。而支持地址重映射的处理器则不同。下面以Samsung的另一款专门针对网络应用的ARM7TDMI处理器S3C4510B为例来介绍。
S3C4510B内部有几个特殊寄存器,用于实现各存储介质在地址空间中的重映射。
(1)SYSCFG:该寄存器决定系统管理器中特殊寄存器的起始地址,以及片内SRAM的使用方式和起始地址。
(2)ROMCON0~ROMCON5:分别对应S3C4510B支持的6个ROM/SRAM/FLASH组。可设置每组的起始物理地址和结束物理地址。
(3)DRAMCON0~DRAMCON3:分别对应S3C4510B所支持的4个DRAM组。可设置每组的起始物理地址和结束物理地址。
可以通过改变ROMCONx和DRAMCONx寄存器中基指针和尾指针的相应位来方便地实现S3C4510B系统中地址空间的重映射。图2是S3C4510B在实际应用中典型的Remap实现。
明确了地址可重映射与不可重映射的关系之后,下面将详细讨论如何在一个不支持Remap的系统中实现JFFS2文件系统。
3 JFFS2文件系统的实现
3.1 添加Flash的Map文件及芯片参数
MAP文件是CCS软件编译后产生的有关DSP用到所有程序、数据及IO空间的一种映射文件。MAP文件主要有两种生成方法,一种是由系统自动生成,默认文件名为所建立的项目名(如XXX为项目名)加上。map后缀xxx.map,另一种在CMD文件中指定生成MAP文件,操作方法为在MEMORY指令前面加上"-m abc.map",文件名可以任意。
在μClinux-dist/linux-2.4.x/drivers/mtd/maps下添加本系统MPU的Map文件s3c44b0x.c.该文件包含了系统中Flash的相关信息,如Flash的起始物理地址、大小、数据总线的宽度、分区、读写函数、初始化和注销程序等。具体配置如下:
(1)定义SST39VF160在系统中的起始地址、大小、总线宽度:
#define WINDOW_ADDR 0x00000000/*从0地址开始*/
#define WINDOW_SIZE 0x00200000/*2MB*/
#define BUSWIDTH 2/*16位*/
(2)定义SST39VF160中字节、半字及字的读写操作函数。
(3)定义SST39VF160中的具体分区。在本系统中ROMFS文件系统是与内核编译在一起的,因此定义了三个分区,分别用于放置引导内核启动的BootLoader程序、经过压缩的系统内核以及需要保存的动态数据。其中第三个区是要实现JFFS2文件系统的分区。
(4)定义用于初始化SST39VF160的int_init init_s3c-44b0x(void)函数。因为S3C44B0X不支持Remap,所以注释掉了与ioremap有关的语句,否则在系统启动时将返回一个错误"Failed to ioremap".另外SST39VF160是遵循JEDEC标准的Flash芯片,在探测时直接采用"jedec_probe".
int _init init_s3c44b0x(void) {
……
/*s3c44b0x_map.map_priv_1 =(unsigned long)ioremap
(WINDOW_ADDR,WINDOW_SIZE);
if(!s3c44b0x_map.map_priv_1) {
printk(″Failed to ioremap\n″);
return -EIO;
}*/
mymtd=do_map_probe(″jedec_probe″,&s3c44b0x_map);
……
/*iounmap((void *)s3c44b0x_map.map_priv_1);*/
……
}
(5)定义用于注销SST39VF160的static void _exit cle-anup_s3c44b0x(void)函数。同理,注释掉了与ioremap有关的语句。
static void _exit cleanup_s3c44b0x(void)
{ ……
/*if (s3c44b0x_map.map_priv_1) {
iounmap((void *)s3c44b0x_map.map_priv_1);
s3c44b0x_map.map_priv_1=0;
}*/
}
(6)由于在linux-2.4.x版本中没有关于SST39VF160的定义,所以需要在μClinux-dist/linux-2.4.x/drivers/mtd/chips/jedec_probe.c中添加SST39VF160的相关信息。
3.2 修改MTD配置文件
MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。CFI接口的MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。
本节将论述地址可重映射与不支持地址重映射的嵌入式系统在实现JFFS2文件系统上的差别。当ARM处理器发生异常时,程序计数器PC会被强制地从异常类型对应的固定存储器地址开始执行程序。这些固定的地址称为异常向量(exception vector)。ARM中异常向量定位在32位地址空间的低端,正常地址范围为:0x00000000~0x0000001C.每个异常向量内存放用户编写的一条跳转指令,可以转到中断服务子程序的首地址。
在嵌入式系统中,为了保证系统上电或复位时Boot-Loader 程序能够首先被加载运行,Flash只能连接到存储空间的0地址处。对于地址可重映射的系统,当系统启动后,可将存放在Flash中的异常向量表的内容拷贝到SDRAM的基地址处,然后修改相应的寄存器,将SDRAM 重映射到0地址。这样系统产生异常时,PC就可以直接从SDRAM中取指令,从而加快了程序的存取速度,缩短了中断的响应时间。
对于不支持地址重映射的系统,异常向量表中的内容只能存放在Flash的0地址处。每次系统进入异常的时候,系统必须从Flash中读取指令。这一点对于实时性要求不高的场合影响不大,但要在这样的系统上实现JFFS2文件系统则会出现问题。具体情况为:对Flash进行擦除(erase/eraseall)或写入(cp/cat/dd)操作时会发生中断,这时系统将强制PC指向异常向量表中的相应位置。在不支持地址重映射的系统中,异常向量表存放在Flash的0地址处。当PC开始从Flash中读取指令时,系统就会死机。这是因为Flash在擦除或写入的时候是不能执行读操作的,否则就会发生不可预料的错误,从而不能完成擦除或写入操作。相反,在支持地址重映射的系统中就不会出现这样的问题。因为它是从SDRAM中读取中断跳转指令的,不会出现在Flash擦除或写入时执行读操作的情况。
为了解决在不支持地址重映射的系统中不能对Flash进行正常擦除或写入的问题,采用了在MTD层的驱动函数的相应位置加关中断和开中断的方法。具体过程如下:
在μClinux-dist/linux-2.4.x/include/asm/arch/hard-ware.h中定义:
#define INT_ENABLE(n) IntMask &=~(1《(n))
#define INT_DISABLE(n) IntMask |=(1《(n))
在μClinux-dist/linux-2.4.x/include/asm/arch/irqs.h中定义:
#define INT_GLOBAL 26 /*总中断允许位*/
对μClinux-dist/linux-2.4.x/drivers/mtd/chips/cfi_cmdset_0002.c文件做如下修改:
#include <asm/arch/hardware.h>
#include <asm/arch/irqs.h>
static inline int do_erase_oneblock(struct map_info *map,
struct flchip *chip,unsigned long adr)
{ ……
INT_DISABLE(INT_GLOBAL);
……
INT_ENABLE(INT_GLOBAL);
……
}
static int do_write_oneword(struct map_info *map,struct flchip *chip,unsigned long adr,_u32 datum,int fast)
{ ……
INT_DISABLE(INT_GLOBAL);
……
INT_ENABLE(INT_GLOBAL);
……
}
3.3 内核配置文件设置
Menuconfig下的配置选项与在支持地址重映射的系统中实现JFFS2时的配置相同。为了避免MTDBLOCK与BLK-MEM主设备号的冲突,将μClinux-dist/linux-2.4.x/drivers/block/blkmem.c与μClinux-dist/linux-2.4.x/includee/linux/major.h中的BLKMEM_MAJOR值从"31"改为"30",然后添加MTD设备节点到/vendors/Samsung/44B0目录下的Makefile文件中。
3.4 内核的编译与启动
以上步骤完成之后,运行内核编译命令,启动内核。在超级终端中将显示:
s3c44b0x flash device:200000 at 0
Found:SST SST39VF160
number of JEDEC chips:1
Creating 3 MTD partitions on ″S3C44B0X flash device″:
0x00000000-0x00020000:″reserved for bootloader(128k)″
mtd:Giving out device 0 to reserved for bootloader(128k)
0x00020000-0x00140000:″kernel(1152K)″
mtd:Giving out device 1 to kernel(1152K)
0x00140000-0x00200000:″jffs2(768K)″
mtd:Giving out device 2 to jffs2(768K)
3.5 创建和拷贝JFFS2映像文件
/> eraseall /dev/mtd2
Erased 768 Kibyte @ 0 - 100% complete.
/> cd /var/tmp
/var/tmp> mkdir jffs2
/var/tmp> mkdir jffs2/file
/var/tmp> mkfs.jffs2 -d jffs2 -o jffs2.img
/var/tmp> cp jffs2.img /dev/mtd2
3.6 Mount JFFS2分区
/var/tmp> mount -t jffs2 /dev/mtdblock2 /mnt
/var/tmp> cd /proc
/proc> cat mounts
……
/dev/mtdblock2 /mnt jffs2 rw 0 0 /*mount成功*/
/proc> cd /mnt
/mnt> ls
file
如果希望μClinux每次启动时,自动将Flash的第三个分区mount到/mnt目录,可以在/vendors/Samsung/44B0目录下的rc文件中加入:mount -t jffs2 /dev/mtdblock2/mnt.
4 结束语
本文讨论了在不支持Remap的系统中建立JFFS2文件系统的必要性和可能性,并结合Samsung的S3C44B0X芯片,通过在MTD Driver中加关中断和开中断的方法实现了在不支持Remap的系统中建立JFFS2文件系统。由于在MTD Driver中关中断和开中断的操作增加了系统的复杂性,因此推荐采用两片Flash:一片NOR型Flash用于存储启动装载程序和内核;一片NAND型Flash用于存储用户的动态数据和应用程序。其中NAND型Flash可以采用新型的YAFFS文件系统。
免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。