原创 jffs文件系统分析

2006-10-19 17:21 5831 8 6 分类: MCU/ 嵌入式





jffs文件系统分析

2005年5月3日  作者:网路  发布人:专业嵌入式网站   颜色默认灰度橄榄色绿色蓝色褐色红色   本文已被浏览 420 次







 jffs文件系统分析     
 


jffs文件系统分析 
一、数据结构分析 
这些结构不会是每一个成员变量都作解释,有的英语注释说的很清楚了,有些会在下面的相关的代码解释 

1、struct jffs_control 
/* A struct for the overall file system control. Pointers to 
jffs_control structs are named `c' in the source code. */ 
struct jffs_control 

struct super_block *sb; /* Reference to the VFS super block. */ 
struct jffs_file *root; /* The root directory file. */ 
struct list_head *hash; /* Hash table for finding files by ino. */ 
struct jffs_fmcontrol *fmc; /* Flash memory control structure. */ 
__u32 hash_len; /* The size of the hash table. */ 
__u32 next_ino; /* Next inode number to use for new files. */ 
__u16 building_fs; /* Is the file system being built right now? */ 
struct jffs_delete_list *delete_list; /* Track deleted files. */ 
pid_t thread_pid; /* GC thread's PID */ 
struct task_struct *gc_task; /* GC task struct */ 
struct completion gc_thread_comp; /* GC thread exit mutex */ 
__u32 gc_minfree_threshold; /* GC trigger thresholds */ 
__u32 gc_maxdirty_threshold; 
__u16 gc_background; /* GC currently running in background */ 
}; 
解释: 
(1)为了快速由inode num找到文件的struct jffs_file结构,所以建立了长度为hash_len的哈西表,hash指向了该哈西表 
(2)在jffs中,不论目录还是普通文件,都有一个struct jffs_file结构表示,成员变量root代表根文件。 
(3)成员变量delete_list是为了删除文件而建立,只是在将文件系统mount到设备上而扫描flash的时候使用。 

2、struct jffs_fmcontrol 
很显然,这是一个描述整个flash使用情况的结构 
struct jffs_fmcontrol 

__u32 flash_size; 
__u32 used_size; 
__u32 dirty_size; 
__u32 free_size; 
__u32 sector_size; 
__u32 min_free_size; /* The minimum free space needed to be able 
to perform garbage collections. */ 
__u32 max_chunk_size; /* The maximum size of a chunk of data. */ 
struct mtd_info *mtd; //指向mtd设备 
struct jffs_control *c; 
struct jffs_fm *head; 
struct jffs_fm *tail; 
struct jffs_fm *head_extra; 
struct jffs_fm *tail_extra; 
struct semaphore biglock; 
}; 
解释: 
(1)整个flash上的空间=flash_size,已经使用了used_size的空间,在used_size中一共有dirty_size是dirty的,dirty也就是说在垃圾回收的时候可以回收的空间,free_size是你能够使用的flash上的空间 
(2)整个flash上的所有used_size是通过一个struct jffs_fm的链表来管理的,head和tail分别指向了最老和最新的flash chunk 
(3)head_extra和tail_extra是在扫描flash的时候使用 
(4)jffs中,对一个节点的数据块的大小是有限制的,最大是max_chunk_size 

3、struct jffs_fm 
/* The struct jffs_fm represents a chunk of data in the flash memory. */ 
struct jffs_fm 

__u32 offset; //在flash中的偏移 
__u32 size; //大小 
struct jffs_fm *prev; //形成双向链表 
struct jffs_fm *next; 
struct jffs_node_ref *nodes; /* USED if != 0. */ 
}; 
解释: 
(1)由于对文件的多次读写,一个struct jffs_fm可能会属于多个struct jffs_node结构,所以成员变量nodes代表了所有属于同一个jffs_fm的jffs_node的链表 
(2)如果nodes==NULL,说明该jffs_fm不和任何node关联,也就是说该fm表示的区域是dirty的。 

4、struct jffs_node 
不论文件或是目录,flash上都是用jffs_raw_inode来表示,而struct jffs_node则是其在内存中的体现 
/* The RAM representation of the node. The names of pointers to 
jffs_nodes are very often just called `n' in the source code. */ 
struct jffs_node 

__u32 ino; /* Inode number. */ 
__u32 version; /* Version number. */ 
__u32 data_offset; /* Logic location of the data to insert. */ 
__u32 data_size; /* The amount of data this node inserts. */ 
__u32 removed_size; /* The amount of data that this node removes. */ 
__u32 fm_offset; /* Physical location of the data in the actual 
flash memory data chunk. */ 
__u8 name_size; /* Size of the name. */ 
struct jffs_fm *fm; /* Physical memory information. */ 
struct jffs_node *version_prev; 
struct jffs_node *version_next; 
struct jffs_node *range_prev; 
struct jffs_node *range_next; 
}; 
解释: 
(1)每一次对文件的写操作都会形成一个新的version的节点,成员变量version表明了该节点的版本号 
(2)一个文件是由若干节点组成,这些节点组成双象链表,所以该结构中的struct jffs_node *得成员变量都是为这些双向链表而设立的 
(3)data_offset是逻辑偏移,也就是文件中的偏移,而fm_offset表明该节点的数据在jffs_fm上的偏移 

5、struct jffs_file 
该结构代表一个文件或者目录 
/* The RAM representation of a file (plain files, directories, 
links, etc.). Pointers to jffs_files are normally named `f' 
in the JFFS source code. */ 
struct jffs_file 

__u32 ino; /* Inode number. */ 
__u32 pino; /* Parent's inode number. */ 
__u32 mode; /* file_type, mode */ 
__u16 uid; /* owner */ 
__u16 gid; /* group */ 
__u32 atime; /* Last access time. */ 
__u32 mtime; /* Last modification time. */ 
__u32 ctime; /* Creation time. */ 
__u8 nsize; /* Name length. */ 
__u8 nlink; /* Number of links. */ 
__u8 deleted; /* Has this file been deleted? */ 
char *name; /* The name of this file; NULL-terminated. */ 
__u32 size; /* The total size of the file's data. */ 
__u32 highest_version; /* The highest version number of this file. */ 
struct jffs_control *c; 
struct jffs_file *parent; /* Reference to the parent directory. */ 
struct jffs_file *children; /* Always NULL for plain files. */ 
struct jffs_file *sibling_prev; /* Siblings in the same directory. */ 
struct jffs_file *sibling_next; 
struct list_head hash; /* hash list. */ 
struct jffs_node *range_head; /* The final data. */ 
struct jffs_node *range_tail; /* The first data. */ 
struct jffs_node *version_head; /* The youngest node. */ 
struct jffs_node *version_tail; /* The oldest node. */ 
}; 
解释: 
(1)一个文件是由一系列不同的版本的节点组成的,而highest_version是最高版本 
(2)一个文件维护两个双向链表,一个反映版本的情况,一个反映文件的区域,version_head和version_tail分别指向了最老和最新的节点,range_head指向文件中逻辑偏移为0的节点,沿着该链表,可以读出整个文件的内容。 
(3)在jffs中,所有的文件形成一个树,树的根是jffs_control结构中的root,它是唯一的。通过每个jffs_file中的parent,children,sibling_prev,sibling_next指针可以把所有文件(包括目录)形成一个树 

6、struct jffs_raw_inode 
这是真正写到flash上的一个表示文件(目录)的一个节点的结构 
/* The JFFS raw inode structure: Used for storage on physical media. */ 
/* Perhaps the uid, gid, atime, mtime and ctime members should have 
more space due to future changes in the Linux kernel. Anyhow, since 
a user of this filesystem probably have to fix a large number of 
other things, we have decided to not be forward compatible. */ 
struct jffs_raw_inode 

__u32 magic; /* A constant magic number. */ 
__u32 ino; /* Inode number. */ 
__u32 pino; /* Parent's inode number. */ 
__u32 version; /* Version number. */ 
__u32 mode; /* The file's type or mode. */ 
__u16 uid; /* The file's owner. */ 
__u16 gid; /* The file's group. */ 
__u32 atime; /* Last access time. */ 
__u32 mtime; /* Last modification time. */ 
__u32 ctime; /* Creation time. */ 
__u32 offset; /* Where to begin to write. */ 
__u32 dsize; /* Size of the node's data. */ 
__u32 rsize; /* How much are going to be replaced? */ 
__u8 nsize; /* Name length. */ 
__u8 nlink; /* Number of links. */ 
__u8 spare : 6; /* For future use. */ 
__u8 rename : 1; /* Rename to a name of an already existing file? */ 
__u8 deleted : 1; /* Has this file been deleted? */ 
__u8 accurate; /* The inode is obsolete if accurate == 0. */ 
__u32 dchksum; /* Checksum for the data. */ 
__u16 nchksum; /* Checksum for the name. */ 
__u16 chksum; /* Checksum for the raw inode. */ 
}; 


一、定义jffs文件系统 
static DECLARE_FSTYPE_DEV(jffs_fs_type, "jffs", jffs_read_super); 

二、注册文件系统 
tatic int __init init_jffs_fs(void) 
这个函数主要是建立struct jffs_fm 和 struct jffs_node的专用的缓冲区队列,然后通过register_filesystem(&jffs_fs_type)注册jffs文件系统。 

三、jffs的挂接 

1、read super 
当通过命令mount -t jffs /dev/mtdblock0 /mnt/flash将文件系统mount到设备上的时候,通过sys_mount系统调用进入内核,并通过具体的文件系统的read_super函数建立起vfs的各种数据结构。 
/* Called by the VFS at mount time to initialize the whole file system. */ 
static struct super_block * jffs_read_super(struct super_block *sb, void *data, int silent) 

kdev_t dev = sb->s_dev; 
struct inode *root_inode; 
struct jffs_control *c; 
//jffs文件系统要求mount的设备必须是mtd 
if (MAJOR(dev) != MTD_BLOCK_MAJOR) { 
printk(KERN_WARNING "JFFS: Trying to mount a " 
"non-mtd device.\n"); 
return 0; 


sb->s_blocksize = PAGE_CACHE_SIZE; //设定块的大小 
sb->s_blocksize_bits = PAGE_CACHE_SHIFT; 
sb->u.generic_sbp = (void *) 0; 
sb->s_maxbytes = 0xFFFFFFFF; //Maximum size of the files 

//通过jffs_build_fs扫描整个flash,然后通过flash上的内容建立完整的文件树,对于jffs文件系统,所有的文件都在ram中有对应的结构,不论该文件是否打开 
/* Build the file system. */ 
if (jffs_build_fs(sb) < 0) { //该函数下面具体分析 
goto jffs_sb_err1; 


/* 
* set up enough so that we can read an inode 
*/ 
sb->s_magic = JFFS_MAGIC_SB_BITMASK; //设置文件系统魔术 
sb->s_op = &jffs_ops; //设置super block的操作方法 

//jffs文件系统最小的inode number是JFFS_MIN_INO="1",这里建立根的inode结构 
//对于一个表示jffs文件的inode结构,inode->u.generic_ip是指向一个表示该文件的struct jffs_file结构。通过jffs_read_inode,可以将根的inode设置好,包括上面的inode->u.generic_ip,还有inode->i_op inode->i_fop 
root_inode = iget(sb, JFFS_MIN_INO); 
if (!root_inode) 
goto jffs_sb_err2; 

//这里建立根的dentry结构 
/* Get the root directory of this file system. */ 
if (!(sb->s_root = d_alloc_root(root_inode))) { 
goto jffs_sb_err3; 


//获得sb中jffs_control的指针 
c = (struct jffs_control *) sb->u.generic_sbp; 

/* Set the Garbage Collection thresholds */ 
//当flash上的free size小于gc_minfree_threshold的时候,会启动垃圾回收,以便释放一些空间 
/* GC if free space goes below 5% of the total size */ 
c->gc_minfree_threshold = c->fmc->flash_size / 20; 

if (c->gc_minfree_threshold < c->fmc->sector_size) 
c->gc_minfree_threshold = c->fmc->sector_size; 
//当flash上的dirty size大于gc_maxdirty_threshold的时候,会启动垃圾回收,以便释放一些空间 
/* GC if dirty space exceeds 33% of the total size. */ 
c->gc_maxdirty_threshold = c->fmc->flash_size / 3; 

if (c->gc_maxdirty_threshold < c->fmc->sector_size) 
c->gc_maxdirty_threshold = c->fmc->sector_size; 

//启动垃圾回收的内核线程 
c->thread_pid = kernel_thread (jffs_garbage_collect_thread, 
(void *) c, 
CLONE_FS | CLONE_FILES | CLONE_SIGHAND); 

return sb; 

jffs_sb_err3: 
iput(root_inode); 
jffs_sb_err2: 
jffs_cleanup_control((struct jffs_control *)sb->u.generic_sbp); 
jffs_sb_err1: 
printk(KERN_WARNING "JFFS: Failed to mount device %s.\n", 
kdevname(dev)); 
return 0; 


2、初始化fs,建立文件树 
/* This is where the file system is built and initialized. */ 
int jffs_build_fs(struct super_block *sb) 

struct jffs_control *c; 
int err = 0; 

//创建jffs_control和jffs_fmcontrol结构,并初始化jffs_control中的哈西表,根据mount的mtd设备,初始化jffs_fmcontrol 
if (!(c = jffs_create_control(sb->s_dev))) { 
return -ENOMEM; 

c->building_fs = 1; //标示目前正在building fs 
c->sb = sb; 

//通过jffs_scan_flash扫描整个flash,建立相关的fs的结构,下面会详细分析 
if ((err = jffs_scan_flash(c)) < 0) { 
if(err == -EAGAIN){ 
//如果发现flipping bits,则重新扫描,所谓flipping bits是由于在erase sector的时候,突然断电而造成flash上该扇区内容不确定 
jffs_cleanup_control(c); //清除发现flipping bits之前创建的结构 
if (!(c = jffs_create_control(sb->s_dev))) { 
return -ENOMEM; 

c->building_fs = 1; 
c->sb = sb; 

if ((err = jffs_scan_flash(c)) < 0) { //重新扫描 
goto jffs_build_fs_fail; 

}else{ 
goto jffs_build_fs_fail; 



//在flash上有所有文件和目录的jffs_raw_inode结构,但是没有根文件的结点,所以我们一般要通过jffs_add_virtual_root手动创建根文件的相关结构。jffs_find_file是通过inode number在哈西表中查找该jffs_file 
if (!jffs_find_file(c, JFFS_MIN_INO)) { 
if ((err = jffs_add_virtual_root(c)) < 0) { 
goto jffs_build_fs_fail; 


//由于各种原因,扫描结束后,可能有些文件是要删除的,下面的代码执行删除任务 
while (c->delete_list) { 
struct jffs_file *f; 
struct jffs_delete_list *delete_list_element; 

if ((f = jffs_find_file(c, c->delete_list->ino))) { 
f->deleted = 1; 

delete_list_element = c->delete_list; 
c->delete_list = c->delete_list->next; 
kfree(delete_list_element); 


//有些节点被标记delete,那么我们要去掉这些deleted nodes 
if ((err = jffs_foreach_file(c, jffs_possibly_delete_file)) < 0) { 
printk(KERN_ERR "JFFS: Failed to remove deleted nodes.\n"); 
goto jffs_build_fs_fail; 

//去掉redundant nodes 
jffs_foreach_file(c, jffs_remove_redundant_nodes); 

//从扫描的所有的jffs_node 和 jffs_file 结构建立文件树 
if ((err = jffs_foreach_file(c, jffs_insert_file_into_tree)) < 0) { 
printk("JFFS: Failed to build tree.\n"); 
goto jffs_build_fs_fail; 

//根据每一个文件的版本链表,建立文件的区域链表 
if ((err = jffs_foreach_file(c, jffs_build_file)) < 0) { 
printk("JFFS: Failed to build file system.\n"); 
goto jffs_build_fs_fail; 

//建立vfs和具体文件系统的关系 
sb->u.generic_sbp = (void *)c; 
c->building_fs = 0; //标示building fs 结束 

return 0; 

jffs_build_fs_fail: 
jffs_cleanup_control(c); 
return err; 
} /* jffs_build_fs() */ 


3、扫描flash 

jffs_scan_flash是一个很长的函数,下面我们只是描述函数的结构 
static int jffs_scan_flash(struct jffs_control *c) 

pos = 0 //pos 表示当前flash上扫描的位置 

通过check_partly_erased_sectors函数检查flipping bits 

while (读到flash最后一个byte) { 

//从当前位置读从一个u32 
switch (flash_read_u32(fmc->mtd, pos)) { 
case JFFS_EMPTY_BITMASK: 
如果读到的字节是JFFS_EMPTY_BITMASK也就是0xffffffff,那么该位置上flash是free的,我们还没有使用它,接着就会用一个4k的buffer去读直到不是JFFS_EMPTY_BITMASK的位置停止。 
case JFFS_DIRTY_BITMASK: 
如果读到的字节是JFFS_DIRTY_BITMASK也就是0x00000000,那么读出所有的连续的0x00000000,分配一个jffs_fm结构表示该区域,但是jffs_fm->nodes为空,也就是标示该区域为dirty,并把该jffs_fm连接到jffs_fmcontrol的双向链表中。一般这种区域是由于到了flash的末尾,剩余的空间不够写一个jffs_raw_inode结构,所以全部写0 

case JFFS_MAGIC_BITMASK: 
找到一个真正的jffs_raw_inode结构,将该raw indoe 读出来,如果是一个bad raw inode(例如校验错误等等),那么分配一个jffs_fm结构表示该区域,但是jffs_fm->nodes为空,也就是标示该区域为dirty;如果是一个good inode,那么建立jffs_node结构和jffs_fm结构,并把该jffs_fm连接到jffs_fmcontrol的双向链表中,然后把jffs_node插入到jffs_file的version list中,表明该node的文件的jffs_file结构先通过哈西表查找,如果没有则创建,一般来说,如果这个jffs_node是扫描到的该文件的第一个节点,那么就需要创建jffs_file结构,此后就可以通过哈西表找到该jffs_file结构。 




解释: 
(1)通过上面的循环,可以建立所有的文件的jffs_file结构,并且version list已经建好,但是range list还没有建立,文件还不能正常读写 
(2)通过上面的循环,可以建立表示flash使用情况的jffs_fmcontrol结构,并且所有的used_size都已经通过jffs_fm联接成链表。 


四、文件打开 

五、文件读写 

六、垃圾回收 



虽然说分析内核代码不流行了,不过还是想借此机会为论坛做一点什么,毕竟我曾经从这里索取太多,还有我也是最近刚开始对内核代码有强烈的兴趣,水平有限,有心杀贼,无力回天,贴子还没写完,不过任何错误的指正都非常的欢迎,多谢各位牛哥 


误会了,其实我内心真正喜欢的是红烧排骨 
PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户1053025 2006-10-20 09:54

呵呵
相关推荐阅读
用户1280303 2008-07-24 14:58
nand flash ECC 算法
ECC简介  由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。为了检测数据的可靠性,在应用NAND...
用户1280303 2008-06-06 09:25
bootload与linux以及根文件系统启动参数传替
lefthand0504-05-08, 16:01嵌入式BootLoader技术内幕(一)作者:詹荣开 (zhanrk@sohu.com) 本文选自:IBM DW 2003年12月30日 本文详细地介...
用户1280303 2007-10-29 18:22
从NANDA闪存中启动u-boot
从NAND闪存中启动U-BOOT的设计2007-05-12 07:48U-BOOT 支持ARM、 PowerPC等多种架构的处理器,也支持Linux、NetBSD和VxWorks等多种操作系统,主要用...
用户1280303 2007-10-29 18:11
增加u-boot命令的编程方法
6.2.5  添加U-Boot命令U-Boot的命令为用户提供了交互功能,并且已经实现了几十个常用的命令。如果开发板需要很特殊的操作,可以添加新的U-Boot命令。U-Boot的每一个命令都是通过U_...
用户1280303 2007-10-29 14:39
mkimage使用详解
uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节...
用户1280303 2007-09-22 11:19
linux 常用指令
linux目录架构/ 根目录/bin 常用的命令 binary file 的目錄/boot 存放系统启动时必须读取的档案,包括核心 (kernel) 在内 /boot/grub/...
EE直播间
更多
我要评论
1
8
关闭 站长推荐上一条 /3 下一条