歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> Linux inode cache機制分析

Linux inode cache機制分析

日期:2017/2/27 9:29:37   编辑:更多Linux
  Linux inode cache機制實現在fs/inode.c文件中。     1.1.Inode的slab分配器緩存   索引節點緩存(inode cache,簡稱icache)機制的實現是以inode對象的slab分配器緩存為基礎的,因此要從物理內存中申請或釋放一個inode對象,都必須通過kmem_cache_alloc()函數和kmem_cache_free()函數來進行。   Inode對象的slab分配緩存由一個kmem_cache_t類型的指針變量inode_cachep來定義。這個slab分配器緩存是在inode cache的初始化函數inode_init()中通過kmem_cache_create()函數來創建的。   Linux在inode.c文件中又定義了兩個封裝函數,來實現從inode_cachep slab分配器緩存中分配一個inode對象或將一個不再使用的inode對象釋放給slab分配器,如下所示:   #define alloc_inode() \   ((strUCt inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL))   static void destroy_inode(struct inode *inode)   {   if (!list_empty(&inode->i_dirty_buffers))   BUG();   kmem_cache_free(inode_cachep, (inode));   }     1.2 和inode對象相關的一些底層操作   源文件inode.c中實現了一些對inode對象的底層基本操作,如下:   (1)clean_inode()——初始化部分inode對象成員域   該函數用來將一個剛從inode_cachep slab分配器中分配得到的inode對象中的某些成員初始化為已知的值(通常為0),但是有一個例外,既鏈接數i_nlink被初始化為1。這是一個靜態的靜態內部函數,因此它只能被inode.c中的其他函數所調用,如:get_empty_inode()和get_new_inode()。   /*   * This just initializes the inode fields   * to known values before returning the inode..   *   * i_sb, i_ino, i_count, i_state and the lists have   * been initialized elsewhere..   */   static void clean_inode(struct inode *inode)   {   static struct address_space_operations empty_aops;   static struct inode_operations empty_iops;   static struct file_operations empty_fops;   memset(&inode->u, 0, sizeof(inode->u));   inode->i_sock = 0;   inode->i_op = &empty_iops;   inode->i_fop = &empty_fops;   inode->i_nlink = 1; /* NOTE!i_nlink被初始化為1 */   atomic_set(&inode->i_writecount, 0);   inode->i_size = 0;   inode->i_generation = 0;   memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));   inode->i_pipe = NULL;   inode->i_bdev = NULL;   inode->i_data.a_ops = &empty_aops;   inode->i_data.host = inode;   inode->i_mapping = &inode->i_data;   }   (2)get_empty_inode()——從slab分配器中分配一個空的inode對象   該函數通過調用alloc_inode宏從slab分配器中分配一個inode對象,然後把除了I_count引用計數和鏈接計數I_nlink之外的所有域都初始化為NULL(部分域的初始化通過調用clean_inode函數來完成),並將這個inode對象鏈入inode_in_use鏈表中。最後返回這個inode對象的指針,如下所示:   struct inode * get_empty_inode(void)   {   static unsigned long last_ino;   struct inode * inode;     inode = alloc_inode();   if (inode)   {   spin_lock(&inode_lock);   inodes_stat.nr_inodes++;   list_add(&inode->i_list, &inode_in_use);   inode->i_sb = NULL;   inode->i_dev = 0;   inode->i_ino = ++last_ino;   inode->i_flags = 0;   atomic_set(&inode->i_count, 1);   inode->i_state = 0;   spin_unlock(&inode_lock);   clean_inode(inode);   }   return inode;   }   Linux內核模塊通常並不會調用這個函數來分配一個inode對象。那些想獲取一個沒有索引節點號的inode對象的內核模塊(如網絡層等),以及那些沒有任何已知信息的fs,通常會用這個函數來獲取一個新的inode對象。   (3) clear_inode()——清除一個inode對象中的內容   在調用destroy_inode()函數釋放一個inode對象之前,通常調用該函數來清除該inode對象中內容,如:使inode引用的緩沖區無效、解除對其它對象的引用等。   /**   * clear_inode - clear an inode   * @inode: inode to clear   *   * This is called by the filesystem to tell us   * that the inode is no longer useful. We just   * terminate it with extreme prejudice.   */   void clear_inode(struct inode *inode)   {   if (!list_empty(&inode->i_dirty_buffers))   invalidate_inode_buffers(inode);     if (inode->i_data.nrpages)   BUG();   if (!(inode->i_state & I_FREEING))   BUG();   if (inode->i_state & I_CLEAR)   BUG();   wait_on_inode(inode);   if (IS_QUOTAINIT(inode))   DQUOT_DROP(inode);   if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->clear_inode)   inode->i_sb->s_op->clear_inode(inode);   if (inode->i_bdev) {   bdput(inode->i_bdev);   inode->i_bdev = NULL;   }   inode->i_state = I_CLEAR;   }     1.3 icache數據結構   Linux通過在inode_cachep slab分配器緩存之上定義各種雙向鏈表來實現inode緩存機制,以便有效地管理內存inode對象。這些鏈表包括:正在使用的inode鏈表、未使用的inode鏈表、inode哈希鏈表和匿名inode的哈希鏈表,他們的定義如下:   static LIST_HEAD(inode_in_use);   static LIST_HEAD(inode_unused);   static struct list_head *inode_hashtable;   static LIST_HEAD(anon_hash_chain); /* for inodes with NULL i_sb */   此外,每個超級塊對象super_block中還有一條被修改過的、且正在使用的inode雙向鏈表s_dirty。   每一個inode對象都會存在於兩個分離的雙向鏈表中:   (1)一個就是inode哈希鏈表inode_hashtable,用來加快inode查找,每個inode對象都通過I_hash指針鏈入哈希鏈表中。   (2)另一個就是inode的“類型”鏈表:   l 如果I_count>0、I_nlink>0且該inode不髒,則這個inode就通過其I_list指針鏈入系統全局的inode_in_use雙向鏈表。   l 如果I_count和I_nlink都大於0,但是這個inode為髒(既I_state域中設置了I_DIRTY標志),則這個inode通過I_list指針鏈入他所屬的super_block對象的s_dirty鏈表。   l 如果I_count=0,則通過其I_list鏈入inode_unused鏈表。   對於那些不屬於任何超級塊對象(即I_sb=NULL)的匿名inode對象,則通過I_hash指針鏈入系統全局的匿名inode哈希鏈表anon_hash_chain。     1.3.1 對inode緩存鏈表的鎖保護   Linux在inode.c中定義了自旋鎖inode_lock,來實現對所有inode緩存鏈表的互斥訪問。也即,任何訪問任意一條inode緩存鏈表的代碼段,都必須通過調用spin_lock()函數持有該自旋鎖,並在結束訪問後通過spin_unlock()釋放該自旋鎖。Inode_lock的定義如下:   Spinlock_t inode_lock=SPIN_LOCK_UNLOCKED;   NOTE!如果要改變一個正在使用的inode對象的I_state域,也必須先持有該自旋鎖。     1.3.2 inode緩存的統計信息   全局變量inodes_stat定義了inode cache的統計信息,主要包括cache中的inode對象總數和其中未使用的inode個數,其定義如下:   struct {   int nr_inodes;   int nr_unused;   int dummy[5];   } inodes_stat;     1.3.3 inode哈希鏈表   inode哈希鏈表的主要用途是加快在icache中查找一個特定的inode對象。指針inode_hashtable指向一組哈希鏈表表頭,所有哈希函數值(記為h)相同的inode對象都通過I_hash指針作為接口組成雙向鏈表,並掛在inode_hashtable[h]這個哈希鏈表表頭之後。所有哈希鏈表表頭都放在一起組成一個數組,該數組的首地址由指針inode_hashtable所指向。   在Linux中,inode哈希鏈表表頭數組是存放在2order個連續的物理頁幀中的,其中,order≥1,且它的值與系統總的物理頁幀數num_physpages的值相關。因此,哈希鏈表表頭的個數為:2order*PAGE_SIZE/sizeof(struct list_head)。由於list_head結構類型的大小是8個字節(2個32位指針),因此:inode哈希鏈表表頭的個數可以表示為:2(order+12-3)。   1 哈希鏈表的初始化   inode cache的初始化工作是由inode_init()函數來完成的,它主要完成兩項工作:(1)inode哈希鏈表的初始化,包括為inode哈希鏈表表頭數組分配物理內存等;(2)創建inode slab分配器緩存。該函數的源代碼如下:   /*   * Initialize the hash tables.   */   void __init inode_init(unsigned long mempages)   {   struct list_head *head;   unsigned long order;   unsigned int nr_hash;   int i;     /*計算order的值,但是我不知道為什麼要這樣計算?:) */   mempages >>= (14 - PAGE_SHIFT);   mempages *= sizeof(struct list_head);   for (order = 0; ((1UL




Copyright © Linux教程網 All Rights Reserved