这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » jffs文件系统分析

共6条 1/1 1 跳转至

jffs文件系统分析

菜鸟
2006-12-09 17:40:52     打赏
一、数据结构分析
这些结构不会是每一个成员变量都作解释,有的英语注释说的很清楚了,有些会在下面的相关的代码解释

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文件系统。




关键词: 文件     系统分析     struct     flash     一个         

菜鸟
2006-12-09 17:42:00     打赏
2楼
三、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联接成链表。
}

四、文件打开

五、文件读写
六、垃圾回收

菜鸟
2007-03-28 14:18:00     打赏
3楼
在Linux环境下,JFFS似乎是不错的选择。但是JFFS启动时速度很慢,YAFFS速度会快很多,内存需求也少些。
如果内存紧张而没有操作系统支持,又要求快速启动,可以试试UFFS。

菜鸟
2007-03-28 16:55:00     打赏
4楼
有垃圾回收部分的说明吗?

菜鸟
2007-03-29 18:25:00     打赏
5楼
ding!

菜鸟
2007-03-29 19:21:00     打赏
6楼

不错


共6条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]