日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

jffs文件系統(tǒng)分析

 imzjw 2007-01-10

申明:這份文檔是按照自由軟件開放源代碼的精神發(fā)布的,任何人可以免費獲得、使用和重新發(fā)布,但是你沒有限制別人重新發(fā)布你發(fā)布內(nèi)容的權(quán)利。發(fā)布本文的目的是希望它能對讀者有用,但沒有任何擔(dān)保,甚至沒有適合特定目的的隱含的擔(dān)保。更詳細(xì)的情況請參閱GNU 通用公共許可證(GPL),以及GNU 自由文檔協(xié)議(GFDL)。

你應(yīng)該已經(jīng)和文檔一起收到一份GNU 通用公共許可證(GPL)的副本。如果還沒有,寫信給:
The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA

歡迎各位指出文檔中的錯誤與疑問


一、flash讀寫的特殊性
對于嵌入式系統(tǒng),flash是很常見的一種設(shè)備,而大部分的嵌入式系統(tǒng)都是把文件系統(tǒng)建立在flash之上,由于對flash操作的特殊性,使得在flash上的文件系統(tǒng)和普通磁盤上的文件系統(tǒng)有很大的差別,對flash操作的特殊性包括:
(1) 不能對單個字節(jié)進(jìn)行擦除,最小的擦寫單位是一個block,有時候也稱為一個扇區(qū)。典型的一個block的大小是64k。不同的flash會有不同,具體參考flash芯片的規(guī)范。
(2) 寫操作只能對一個原來是空(也就是該地址的內(nèi)容是全f)的位置操作,如果該位置非空,寫操作不起作用,也就是說如果要改寫一個原來已經(jīng)有內(nèi)容的空間,只能是讀出該sector到ram,在ram中改寫,然后寫整個sector。
由于這些特殊寫,所以在flash這樣的設(shè)備上建立文件也有自己獨特的特點,下面我們就以jffs為例進(jìn)行分析。

二、jffs體系結(jié)構(gòu)介紹
1、存儲結(jié)構(gòu)
在jffs中,所有的文件和目錄是一樣對待的,都是用一個jffs_raw_inode來表示


整個flash上就是由一個一個的raw inode排列組成,一個目錄只有一個raw inode,對于文件則是由一個或多個raw inode組成。

2、文件組成
在文件系統(tǒng)mount到flash設(shè)備上的時候,會掃描flash,從而根據(jù)flash上的所有屬于一個文件的raw inode建立一個jffs_file結(jié)構(gòu)以及node list。
下面的圖顯示了一個文件的組成


一個文件是由若干個jffs_node組成,每一個jffs_node是根據(jù)flash上得jffs_raw_inode而建立的,jffs_file主要維護(hù)兩個鏈表
版本鏈表:主要是描述該node創(chuàng)建的早晚,就是說version_head指向的是一個最老的node,也就意味著垃圾回收的時候最該回收的就是這個最老的node。
區(qū)域鏈表:這個鏈表主要是為讀寫文件創(chuàng)建的,version_head指向的node代表的文件數(shù)據(jù)區(qū)域是0~~~n-1 之后依次的節(jié)點分別是 n~~~m-1 m~~~~o-1 …….其中n

3、操作
對文件的讀操作應(yīng)該是比較簡單,但是寫操作,包括更改文件名等操作都是引起一個新的jffs_node的誕生,同時要寫一個相映的raw inode到flash上,這樣的操作有可能導(dǎo)致前面的某個jffs_node上面的數(shù)據(jù)完全失效,從而導(dǎo)致對應(yīng)flash上的raw inode的空間成為dirty。
下面舉一個例子可能會更清楚一些。

一個文件的range list是由上面的三個jffs_node組成,當(dāng)我們做如下寫操作的時候
lseek( fd, 10, SEEK_SET );
write( fd, buf,40 );
第一個和最后一個node被截短了,第二個node完全被新數(shù)據(jù)替換,該node會從鏈表上摘下來,flash上空間變成dirty。如果做如下寫操作的時候
lseek( fd, 23, SEEK_SET );
write( fd, buf,5 );
此時,第二個node被分裂成兩個node,同時產(chǎn)生一個新的node,range鏈表的元素變成五個。


4、垃圾回收

我們的flash上的內(nèi)容基本上是有兩種情況,一種是前面一段是used,后面是free的,還有一個是中間一段是used,flash的開始和底部都是free的,但是不管如何,如果符合了垃圾回收的條件,就要啟動垃圾回收。Used的空間中,有一部分是我們真正需要的數(shù)據(jù),還有一部分由于我們的對文件的寫,刪除等操作而變成dirty的空間,我們垃圾回收的目標(biāo)就是把這些dirty的空間變成free的,從而可以繼續(xù)使用。

Used的空間是有一個個的jffs_fm的鏈表組成,垃圾回收也總是從used的最頂部開始,如果jffs_fm不和任何的jffs_node相關(guān),那么我們就認(rèn)為jffs_fm代表的這塊flash空間是dirty的,找到了完整的至少一個sector的dirty空間,就可以把這個sector擦掉,從而增加一個sector的free空間。

三、數(shù)據(jù)結(jié)構(gòu)分析
這些結(jié)構(gòu)不會是每一個成員變量都作解釋,有的英語注釋說的很清楚了,有些會在下面的相關(guān)的代碼解釋

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結(jié)構(gòu),所以建立了長度為hash_len的哈西表,hash指向了該哈西表
(2)在jffs中,不論目錄還是普通文件,都有一個struct jffs_file結(jié)構(gòu)表示,成員變量root代表根文件。
(3)成員變量delete_list是為了刪除文件而建立,只是在將文件系統(tǒng)mount到設(shè)備上而掃描flash的時候使用。
(4)文件號最小是1,分配給了根,此后,每創(chuàng)建一個文件next_no就會加一。當(dāng)文件刪除之后,也不會回收文件號,畢竟當(dāng)next_no到達(dá)最大值的時候,flash恐怕早就掛拉。

2、struct jffs_fmcontrol
很顯然,這是一個描述整個flash使用情況的結(jié)構(gòu)
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設(shè)備
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,已經(jīng)使用了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中,對一個節(jié)點的數(shù)據(jù)塊的大小是有限制的,最大是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結(jié)構(gòu),所以成員變量nodes代表了所有屬于同一個jffs_fm的jffs_node的鏈表
(2)如果nodes==NULL,說明該jffs_fm不和任何node關(guān)聯(lián),也就是說該fm表示的區(qū)域是dirty的。

4、struct jffs_node
不論文件或是目錄,flash上都是用jffs_raw_inode來表示,而struct jffs_node則是其在內(nèi)存中的體現(xiàn)
/* 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的節(jié)點,成員變量version表明了該節(jié)點的版本號,創(chuàng)建第一個node的時候,verion = 1,此后由于對文件的寫操作,而創(chuàng)建新的node的時候,version就會加一。同文件號的道理一樣,version也不會回收。
(2)一個文件是由若干節(jié)點組成,這些節(jié)點組成雙象鏈表,所以該結(jié)構(gòu)中的struct jffs_node *得成員變量都是為這些雙向鏈表而設(shè)立的
(3)data_offset是邏輯偏移,也就是文件中的偏移,而fm_offset表明該節(jié)點的數(shù)據(jù)在jffs_fm上的偏移

5、struct jffs_file
該結(jié)構(gòu)代表一個文件或者目錄
/* 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)一個文件是由一系列不同的版本的節(jié)點組成的,而highest_version是最高版本。
(2)一個文件維護(hù)兩個雙向鏈表,一個反映版本的情況,一個反映文件的區(qū)域,version_head和version_tail分別指向了最老和最新的節(jié)點,range_head指向文件中邏輯偏移為0的節(jié)點,沿著該鏈表,可以讀出整個文件的內(nèi)容。
(3)在jffs中,所有的文件形成一個樹,樹的根是jffs_control結(jié)構(gòu)中的root,它是唯一的。通過每個jffs_file中的parent,children,sibling_prev,sibling_next指針可以把所有文件(包括目錄)形成一個樹

6、struct jffs_raw_inode
這是真正寫到flash上的一個表示文件(目錄)的一個節(jié)點的結(jié)構(gòu)
/* 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的掛接

1、定義jffs文件系統(tǒng)
static DECLARE_FSTYPE_DEV(jffs_fs_type, "jffs", jffs_read_super);

2、注冊文件系統(tǒng)
tatic int __init init_jffs_fs(void)
這個函數(shù)主要是建立struct jffs_fm 和 struct jffs_node的專用的緩沖區(qū)隊列,然后通過register_filesystem(&jffs_fs_type)注冊jffs文件系統(tǒng)。


3、read super
當(dāng)通過命令mount -t jffs /dev/mtdblock0 /mnt/flash將文件系統(tǒng)mount到設(shè)備上的時候,通過sys_mount系統(tǒng)調(diào)用進(jìn)入內(nèi)核,并通過具體的文件系統(tǒng)的read_super函數(shù)建立起vfs的各種數(shù)據(jù)結(jié)構(gòu)。
/* 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文件系統(tǒng)要求mount的設(shè)備必須是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; //設(shè)定塊的大小
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上的內(nèi)容建立完整的文件樹,對于jffs文件系統(tǒng),所有的文件都在ram中有對應(yīng)的結(jié)構(gòu),不論該文件是否打開
/* Build the file system. */
if (jffs_build_fs(sb) < 0) { //該函數(shù)下面具體分析
goto jffs_sb_err1;
}

/*
* set up enough so that we can read an inode
*/
sb->s_magic = JFFS_MAGIC_SB_BITMASK; //設(shè)置文件系統(tǒng)魔術(shù)
sb->s_op = &jffs_ops; //設(shè)置super block的操作方法

//jffs文件系統(tǒng)最小的inode number是JFFS_MIN_INO=1,這里建立根的inode結(jié)構(gòu)
//對于一個表示jffs文件的inode結(jié)構(gòu),inode->u.generic_ip是指向一個表示該文件的struct jffs_file結(jié)構(gòu)。通過jffs_read_inode,可以將根的inode設(shè)置好,包括上面的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結(jié)構(gòu)
/* 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 */
//當(dāng)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;
//當(dāng)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;

//啟動垃圾回收的內(nèi)核線程
c->thread_pid = kernel_thread (jffs_garbage_collect_thread,
(void *) c,
CLONE_FS | CLONE_FILES | CLONE_SIGHAND);

return sb;
}

4、初始化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;

//創(chuàng)建jffs_control和jffs_fmcontrol結(jié)構(gòu),并初始化jffs_control中的哈西表,根據(jù)mount的mtd設(shè)備,初始化jffs_fmcontrol
if (!(c = jffs_create_control(sb->s_dev))) {
return -ENOMEM;
}
c->building_fs = 1; //標(biāo)示目前正在building fs
c->sb = sb;

//通過jffs_scan_flash掃描整個flash,建立相關(guān)的fs的結(jié)構(gòu),下面會詳細(xì)分析
if ((err = jffs_scan_flash(c)) < 0) {
if(err == -EAGAIN){
//如果發(fā)現(xiàn)flipping bits,則重新掃描,所謂flipping bits是由于在erase sector的時候,突然斷電而造成flash上該扇區(qū)內(nèi)容不確定
jffs_cleanup_control(c); //清除發(fā)現(xiàn)flipping bits之前創(chuàng)建的結(jié)構(gòu)
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結(jié)構(gòu),但是沒有根文件的結(jié)點,所以我們一般要通過jffs_add_virtual_root手動創(chuàng)建根文件的相關(guān)結(jié)構(gòu)。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;
}
}
//由于各種原因,掃描結(jié)束后,可能有些文件是要刪除的,下面的代碼執(zhí)行刪除任務(wù)
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);
}

//有些節(jié)點被標(biāo)記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 結(jié)構(gòu)建立文件樹
if ((err = jffs_foreach_file(c, jffs_insert_file_into_tree)) < 0) {
printk("JFFS: Failed to build tree.\n");
goto jffs_build_fs_fail;
}
//根據(jù)每一個文件的版本鏈表,建立文件的區(qū)域鏈表
if ((err = jffs_foreach_file(c, jffs_build_file)) < 0) {
printk("JFFS: Failed to build file system.\n");
goto jffs_build_fs_fail;
}
//建立vfs和具體文件系統(tǒng)的關(guān)系
sb->u.generic_sbp = (void *)c;
c->building_fs = 0; //標(biāo)示building fs 結(jié)束

return 0;

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


5、掃描flash

jffs_scan_flash是一個很長的函數(shù),下面我們只是描述函數(shù)的結(jié)構(gòu)
static int jffs_scan_flash(struct jffs_control *c)
{
pos = 0 //pos 表示當(dāng)前flash上掃描的位置

通過check_partly_erased_sectors函數(shù)檢查flipping bits

while (讀到flash最后一個byte) {

//從當(dāng)前位置讀從一個u32
switch (flash_read_u32(fmc->mtd, pos)) {
case JFFS_EMPTY_BITMASK:
如果讀到的字節(jié)是JFFS_EMPTY_BITMASK也就是0xffffffff,那么該位置上flash是free的,我們還沒有使用它,接著就會用一個4k的buffer去讀直到不是JFFS_EMPTY_BITMASK的位置停止。
case JFFS_DIRTY_BITMASK:
如果讀到的字節(jié)是JFFS_DIRTY_BITMASK也就是0x00000000,那么讀出所有的連續(xù)的0x00000000,分配一個jffs_fm結(jié)構(gòu)表示該區(qū)域,但是jffs_fm->nodes為空,也就是標(biāo)示該區(qū)域為dirty,并把該jffs_fm連接到j(luò)ffs_fmcontrol的雙向鏈表中。一般這種區(qū)域是由于到了flash的末尾,剩余的空間不夠?qū)懸粋€jffs_raw_inode結(jié)構(gòu),所以全部寫0

case JFFS_MAGIC_BITMASK:
找到一個真正的jffs_raw_inode結(jié)構(gòu),將該raw indoe 讀出來,如果是一個bad raw inode(例如校驗錯誤等等),那么分配一個jffs_fm結(jié)構(gòu)表示該區(qū)域,但是jffs_fm->nodes為空,也就是標(biāo)示該區(qū)域為dirty;如果是一個good inode,那么建立jffs_node結(jié)構(gòu)和jffs_fm結(jié)構(gòu),并把該jffs_fm連接到j(luò)ffs_fmcontrol的雙向鏈表中,然后把jffs_node插入到j(luò)ffs_file的version list中,表明該node的文件的jffs_file結(jié)構(gòu)先通過哈西表查找,如果沒有則創(chuàng)建,一般來說,如果這個jffs_node是掃描到的該文件的第一個節(jié)點,那么就需要創(chuàng)建jffs_file結(jié)構(gòu),此后就可以通過哈西表找到該jffs_file結(jié)構(gòu)。

}

}
解釋:
(1)通過上面的循環(huán),可以建立所有的文件的jffs_file結(jié)構(gòu),并且version list已經(jīng)建好,但是range list還沒有建立,文件還不能正常讀寫
(2)通過上面的循環(huán),可以建立表示flash使用情況的jffs_fmcontrol結(jié)構(gòu),并且所有的used_size都已經(jīng)通過jffs_fm聯(lián)接成鏈表。
}

五、文件打開
本身文件的打開對jffs文件系統(tǒng)下的文件是沒有什么實際的意義,因為在mount的時候就會scan整個flash而建立文件樹,所有的文件都其實是打開的了。需要留意的是:
1、創(chuàng)建一個文件

可以通過open函數(shù)創(chuàng)建一個文件,只要設(shè)定相映的flag。本操作是通過jffs_create完成,很顯然,該函數(shù)最直觀的效果是向flash寫入一個jffs_raw_inode及其文件名,當(dāng)然也要維護(hù)文件樹的完整性。
static int jffs_create(struct inode *dir, struct dentry *dentry, int mode)
{

//獲得create文件的那個目錄的jffs_file結(jié)構(gòu),我們前面說過inode結(jié)構(gòu)的inode->u.generic_ip指向她的jffs_file結(jié)構(gòu)。
dir_f = (struct jffs_file *)dir->u.generic_ip;

c = dir_f->c;

//分配一jffs_node的結(jié)構(gòu),該結(jié)構(gòu)是該jffs_file的第一個node
if (!(node = jffs_alloc_node())) {
D(printk("jffs_create(): Allocation failed: node == 0\n"));
return -ENOMEM;
}

down(&c->fmc->biglock);

node->data_offset = 0;
node->removed_size = 0;

//初始化向flash上寫的jffs_raw_inode結(jié)構(gòu)
raw_inode.magic = JFFS_MAGIC_BITMASK;
raw_inode.version = 1; //第一個version是一
。。。略過部分代碼
raw_inode.deleted = 0;

//將raw inode 和文件名寫進(jìn)flash
if ((err = jffs_write_node(c, node, &raw_inode,
dentry->d_name.name, 0, 0, NULL)) < 0) {
D(printk("jffs_create(): jffs_write_node() failed.\n"));
jffs_free_node(node);
goto jffs_create_end;
}

//在jffs_insert_node中,建立jffs_file和這個新產(chǎn)生的node的關(guān)系
if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name,
node)) < 0) {
goto jffs_create_end;
}

/* Initialize an inode. */
inode = jffs_new_inode(dir, &raw_inode, &err);
if (inode == NULL) {
goto jffs_create_end;
}
err = 0;
//設(shè)定各種操作函數(shù)集合
inode->i_op = &jffs_file_inode_operations;
inode->i_fop = &jffs_file_operations;
inode->i_mapping->a_ops = &jffs_address_operations;
inode->i_mapping->nrpages = 0;

d_instantiate(dentry, inode);

} /* jffs_create() */

2、jffs_lookup

在打開文件的過程中,需要在目錄中搜索,這里調(diào)用jffs_lookup
static struct dentry *
jffs_lookup(struct inode *dir, struct dentry *dentry)
{

struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp;
struct inode *inode = NULL;
len = dentry->d_name.len;
name = dentry->d_name.name;

down(&c->fmc->biglock);

r = -ENAMETOOLONG;
if (len > JFFS_MAX_NAME_LEN) { //名字是否超過jffs要求的最大值
goto jffs_lookup_end;
}

r = -EACCES;
//獲得目錄的jffs_file結(jié)構(gòu)
if (!(d = (struct jffs_file *)dir->u.generic_ip)) {
D(printk("jffs_lookup(): No such inode! (%lu)\n",
dir->i_ino));
goto jffs_lookup_end;
}

//下面的用注視代替了原碼
if ((len == 1) && (name[0] == ‘.‘)) {
處理當(dāng)前目錄.的情況,因為目錄的jffs_file已經(jīng)找到,所以直接調(diào)用iget找到它的inode結(jié)構(gòu)
} else if ((len == 2) && (name[0] == ‘.‘) && (name[1] == ‘.‘)) {
處理..的情況,上層目錄的文件號可以通過jffs_file的pino找到,所以調(diào)用iget找到它上層目錄的inode結(jié)構(gòu)
} else if ((f = jffs_find_child(d, name, len))) {
正常情況,通過jffs_find_child找到該文件的jffs_file結(jié)構(gòu),也就找到了文件號,于是可以通過iget函數(shù)根據(jù)文件號找到該文件的inode結(jié)構(gòu)
} else {
找不到文件
}

//維護(hù)vfs結(jié)構(gòu)的一致性
d_add(dentry, inode);

up(&c->fmc->biglock);
return NULL;

} /* jffs_lookup() */

3、truncate
對于truncate,是通過jffs_setattr實現(xiàn),此處掠過

4、generic_file_open
一般的文件打開是調(diào)用generic_file_open。

六、文件讀寫
對jffs的文件的讀寫是使用page cache的,jffs層上具體的讀函數(shù)使用了通用的generic_file_open,address_space結(jié)構(gòu)描述了page cache中的頁面,對于頁面的操作,jffs是這樣定義的
static struct address_space_operations jffs_address_operations = {
readpage: jffs_readpage,
prepare_write: jffs_prepare_write,
commit_write: jffs_commit_write,
};

static int jffs_readpage(struct file *file, struct page *page)
{
這個函數(shù)很簡單,通過jffs_read_data讀出一個頁面大小的內(nèi)容
}

int jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset,
__u32 size)
{
//寫的偏移不能大于文件的大小
if (read_offset >= f->size) {
D(printk(" f->size: %d\n", f->size));
return 0;
}

//首先要沿著range list找到該offset所在的node
node = f->range_head;
while (pos <= read_offset) {
node_offset = read_offset - pos;
if (node_offset >= node->data_size) {
pos += node->data_size;
node = node->range_next;
}
else {
break;
}
}

//下面的循環(huán)讀入緩沖區(qū)
while (node && (read_data < size)) {
int r;
if (!node->fm) {
/* This node does not refer to real data. */
r = min(size - read_data,
node->data_size - node_offset);
memset(&buf[read_data], 0, r);
}
從offset所在的node開始,讀出一個頁面的數(shù)據(jù),根據(jù)node的data_size的大小,可能一個node就高定,也許要讀一系列的node
else if ((r = jffs_get_node_data(f, node, &buf[read_data],
node_offset,
size - read_data,
f->c->sb->s_dev)) < 0) {
return r;
}
read_data += r;
node_offset = 0;
node = node->range_next;
}

return read_data;
}

對于jffs_prepare_write,只是保證該頁面是正確的,如果需要更新,那末就重新讀入該頁
static ssize_t jffs_prepare_write(struct file *filp, struct page *page,
unsigned from, unsigned to)
{
if (!Page_Uptodate(page) && (from || to < PAGE_CACHE_SIZE))
return jffs_do_readpage_nolock(filp, page);

return 0;
} /* jffs_prepare_write() */

static ssize_t jffs_commit_write(struct file *filp, struct page *page,
unsigned from, unsigned to)
{
void *addr = page_address(page) + from;

loff_t pos = (page->index<

return jffs_file_write(filp, addr, to-from, &pos);
} /* jffs_commit_write() */

static ssize_t jffs_file_write(struct file *filp, const char *buf, size_t count,
loff_t *ppos)
{

err = -EINVAL;

//檢查是否是正規(guī)文件
if (!S_ISREG(inode->i_mode)) {
D(printk("jffs_file_write(): inode->i_mode == 0x%08x\n",
inode->i_mode));
goto out_isem;
}
//只要是正常打開的文件inode->u.generic_ip應(yīng)該指向她的jffs_file結(jié)構(gòu)
if (!(f = (struct jffs_file *)inode->u.generic_ip)) {
D(printk("jffs_file_write(): inode->u.generic_ip = 0x%p\n",
inode->u.generic_ip));
goto out_isem;
}

c = f->c;

//因為對文件node的大小有限制,所以如果寫的數(shù)目非常大,那么我們會產(chǎn)生若干個node來完成一次的寫操作,this_count就是本次寫操作的數(shù)據(jù)量
thiscount = min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count);
//通過一個while循環(huán),完成所有的寫操作
while (count) {
//分配一個jffs_node結(jié)構(gòu)
if (!(node = jffs_alloc_node())) {
err = -ENOMEM;
goto out;
}
//設(shè)定該node的在整個文件的邏輯偏移
node->data_offset = pos;
node->removed_size = 0;

//初始化raw_node
raw_inode.magic = JFFS_MAGIC_BITMASK;
。。。。略過部分代碼
raw_inode.deleted = 0;

//我們在某一個文件偏移位置寫入數(shù)據(jù),除非是在文件末尾,要不然的話,我們需要覆蓋調(diào)部分內(nèi)容,remove_size指明了該node要覆蓋的數(shù)據(jù)的大小,也就是說從該文件偏移處起,前面節(jié)點的remove_size大小的空間要被remove,數(shù)據(jù)將被新的node代替。
if (pos < f->size) {
node->removed_size = raw_inode.rsize = min(thiscount, (__u32)(f->size - pos));

}

//通過jffs_write_node將jffs_raw_inode 文件名 數(shù)據(jù)寫入flash
if ((err = jffs_write_node(c, node, &raw_inode, f->name,
(const unsigned char *)buf,
recoverable, f)) < 0) {
jffs_free_node(node);
goto out;
}
//調(diào)整位置
written += err;
buf += err;
count -= err;
pos += err;

//將新生成的node插入到j(luò)ffs_file結(jié)構(gòu)的node list中
if ((err = jffs_insert_node(c, f, &raw_inode, 0, node)) < 0) {
goto out;
}

thiscount = min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count);
}
out:
up(&c->fmc->biglock);

//更新vfs結(jié)構(gòu)上的信息
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_blocks = (inode->i_size + 511) >> 9;
}
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
//將該文件的inode掛入sb的dirty list
mark_inode_dirty(inode);
invalidate_inode_pages(inode);

out_isem:
return err;
} /* jffs_file_write() */

七、垃圾回收
1、垃圾回收的內(nèi)核線程
int jffs_garbage_collect_thread(void *ptr)
{
//主循環(huán)
for (; {
//看看是否需要睡眠,一般有兩種情況,一種是自由空間的數(shù)量 < MIN_FREE_BYTES 同時至少有一個sector的flash空間是dirty的,還有一種情況是dirty空間的數(shù)量 > MAX_DIRTY_BYTES
if (!thread_should_wake(c))
set_current_state (TASK_INTERRUPTIBLE);
//我們垃圾回收的內(nèi)核線程優(yōu)先級很低,調(diào)用schedule看一看是否有其他進(jìn)程的可以調(diào)度
schedule();
//信號處理部分
while (signal_pending(current)) {
switch(signr) {
case SIGSTOP:
set_current_state(TASK_STOPPED);
schedule();
break;
case SIGKILL:
c->gc_task = NULL;
complete_and_exit(&c->gc_thread_comp, 0);
case SIGHUP:
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
break;
}
}
down(&fmc->biglock);
c->gc_background = 1;
//如果從垃圾回收點處開始,有全部是dirty的sector,那么將通過jffs_try_to_erase將該扇區(qū)擦除,變?yōu)閒ree
if ((erased = jffs_try_to_erase(c)) < 0) {
printk(KERN_WARNING "JFFS: Error in "
"garbage collector: %ld.\n", erased);
}
//只要至少擦掉一個sector,我們就結(jié)束gc thread
if (erased)
goto gc_end;
//如果自由空間等于0,沒辦法拉,只好自殺
if (fmc->free_size == 0) {
send_sig(SIGQUIT, c->gc_task, 1);
goto gc_end;
}
//如果執(zhí)行到此處,則說明具備垃圾回收的條件,但是從垃圾回收點處開始的那一個sector不是完全dirty的,需要搬移部分的raw inode
if ((result = jffs_garbage_collect_next(c)) < 0) {
printk(KERN_ERR "JFFS: Something "
"has gone seriously wrong "
"with a garbage collect: %d\n", result);
}
//至此至少回收了一個sector 本次任務(wù)結(jié)束,繼續(xù)睡眠
gc_end:
c->gc_background = 0;
up(&fmc->biglock);
} /* for (; */
} /* jffs_garbage_collect_thread() */

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多