申明:這份文檔是按照自由軟件開放源代碼的精神發(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() */
|