乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

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

       imzjw 2007-01-10

      申明:這份文檔是按照自由軟件開放源代碼的精神發(fā)布的,任何人可以免費(fèi)獲得、使用和重新發(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) 不能對單個(gè)字節(jié)進(jìn)行擦除,最小的擦寫單位是一個(gè)block,有時(shí)候也稱為一個(gè)扇區(qū)。典型的一個(gè)block的大小是64k。不同的flash會有不同,具體參考flash芯片的規(guī)范。
      (2) 寫操作只能對一個(gè)原來是空(也就是該地址的內(nèi)容是全f)的位置操作,如果該位置非空,寫操作不起作用,也就是說如果要改寫一個(gè)原來已經(jīng)有內(nèi)容的空間,只能是讀出該sector到ram,在ram中改寫,然后寫整個(gè)sector。
      由于這些特殊寫,所以在flash這樣的設(shè)備上建立文件也有自己獨(dú)特的特點(diǎn),下面我們就以jffs為例進(jìn)行分析。

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


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

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


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

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

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


      4、垃圾回收

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

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

      三、數(shù)據(jù)結(jié)構(gòu)分析
      這些結(jié)構(gòu)不會是每一個(gè)成員變量都作解釋,有的英語注釋說的很清楚了,有些會在下面的相關(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中,不論目錄還是普通文件,都有一個(gè)struct jffs_file結(jié)構(gòu)表示,成員變量root代表根文件。
      (3)成員變量delete_list是為了刪除文件而建立,只是在將文件系統(tǒng)mount到設(shè)備上而掃描flash的時(shí)候使用。
      (4)文件號最小是1,分配給了根,此后,每創(chuàng)建一個(gè)文件next_no就會加一。當(dāng)文件刪除之后,也不會回收文件號,畢竟當(dāng)next_no到達(dá)最大值的時(shí)候,flash恐怕早就掛拉。

      2、struct jffs_fmcontrol
      很顯然,這是一個(gè)描述整個(gè)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)整個(gè)flash上的空間=flash_size,已經(jīng)使用了used_size的空間,在used_size中一共有dirty_size是dirty的,dirty也就是說在垃圾回收的時(shí)候可以回收的空間,free_size是你能夠使用的flash上的空間
      (2)整個(gè)flash上的所有used_size是通過一個(gè)struct jffs_fm的鏈表來管理的,head和tail分別指向了最老和最新的flash chunk
      (3)head_extra和tail_extra是在掃描flash的時(shí)候使用
      (4)jffs中,對一個(gè)節(jié)點(diǎn)的數(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)由于對文件的多次讀寫,一個(gè)struct jffs_fm可能會屬于多個(gè)struct jffs_node結(jié)構(gòu),所以成員變量nodes代表了所有屬于同一個(gè)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)每一次對文件的寫操作都會形成一個(gè)新的version的節(jié)點(diǎn),成員變量version表明了該節(jié)點(diǎn)的版本號,創(chuàng)建第一個(gè)node的時(shí)候,verion = 1,此后由于對文件的寫操作,而創(chuàng)建新的node的時(shí)候,version就會加一。同文件號的道理一樣,version也不會回收。
      (2)一個(gè)文件是由若干節(jié)點(diǎn)組成,這些節(jié)點(diǎn)組成雙象鏈表,所以該結(jié)構(gòu)中的struct jffs_node *得成員變量都是為這些雙向鏈表而設(shè)立的
      (3)data_offset是邏輯偏移,也就是文件中的偏移,而fm_offset表明該節(jié)點(diǎn)的數(shù)據(jù)在jffs_fm上的偏移

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

      6、struct jffs_raw_inode
      這是真正寫到flash上的一個(gè)表示文件(目錄)的一個(gè)節(jié)點(diǎn)的結(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)
      這個(gè)函數(shù)主要是建立struct jffs_fm 和 struct jffs_node的專用的緩沖區(qū)隊(duì)列,然后通過register_filesystem(&jffs_fs_type)注冊jffs文件系統(tǒng)。


      3、read super
      當(dāng)通過命令mount -t jffs /dev/mtdblock0 /mnt/flash將文件系統(tǒng)mount到設(shè)備上的時(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掃描整個(gè)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)
      //對于一個(gè)表示jffs文件的inode結(jié)構(gòu),inode->u.generic_ip是指向一個(gè)表示該文件的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的時(shí)候,會啟動垃圾回收,以便釋放一些空間
      /* 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的時(shí)候,會啟動垃圾回收,以便釋放一些空間
      /* 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掃描整個(gè)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的時(shí)候,突然斷電而造成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é)點(diǎn),所以我們一般要通過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é)點(diǎn)被標(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ù)每一個(gè)文件的版本鏈表,建立文件的區(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是一個(gè)很長的函數(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最后一個(gè)byte) {

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

      case JFFS_MAGIC_BITMASK:
      找到一個(gè)真正的jffs_raw_inode結(jié)構(gòu),將該raw indoe 讀出來,如果是一個(gè)bad raw inode(例如校驗(yàn)錯誤等等),那么分配一個(gè)jffs_fm結(jié)構(gòu)表示該區(qū)域,但是jffs_fm->nodes為空,也就是標(biāo)示該區(qū)域?yàn)閐irty;如果是一個(gè)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)建,一般來說,如果這個(gè)jffs_node是掃描到的該文件的第一個(gè)節(jié)點(diǎn),那么就需要創(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)下的文件是沒有什么實(shí)際的意義,因?yàn)樵趍ount的時(shí)候就會scan整個(gè)flash而建立文件樹,所有的文件都其實(shí)是打開的了。需要留意的是:
      1、創(chuàng)建一個(gè)文件

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

      //獲得create文件的那個(gè)目錄的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的第一個(gè)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; //第一個(gè)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和這個(gè)新產(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)前目錄.的情況,因?yàn)槟夸浀膉ffs_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實(shí)現(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)
      {
      這個(gè)函數(shù)很簡單,通過jffs_read_data讀出一個(gè)頁面大小的內(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開始,讀出一個(gè)頁面的數(shù)據(jù),根據(jù)node的data_size的大小,可能一個(gè)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;

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

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

      //我們在某一個(gè)文件偏移位置寫入數(shù)據(jù),除非是在文件末尾,要不然的話,我們需要覆蓋調(diào)部分內(nèi)容,remove_size指明了該node要覆蓋的數(shù)據(jù)的大小,也就是說從該文件偏移處起,前面節(jié)點(diǎn)的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 同時(shí)至少有一個(gè)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;
      //如果從垃圾回收點(diǎn)處開始,有全部是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);
      }
      //只要至少擦掉一個(gè)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í)行到此處,則說明具備垃圾回收的條件,但是從垃圾回收點(diǎn)處開始的那一個(gè)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);
      }
      //至此至少回收了一個(gè)sector 本次任務(wù)結(jié)束,繼續(xù)睡眠
      gc_end:
      c->gc_background = 0;
      up(&fmc->biglock);
      } /* for (; */
      } /* jffs_garbage_collect_thread() */

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多