歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> Linux2.4目錄項緩存dcache機制的實現分析

Linux2.4目錄項緩存dcache機制的實現分析

日期:2017/2/27 9:29:42   编辑:更多Linux
  Linux用數據結構dentry來描述fs中與某個文件索引節點相鏈接的一個目錄項(可以是文件,也可以是目錄)。   每個dentry對象都屬於下列幾種狀態之一:   (1)未使用(unused)狀態:該dentry對象的引用計數d_count的值為0,但其d_inode指針仍然指向相關的的索引節點。該目錄項仍然包含有效的信息,只是當前沒有人引用他。這種dentry對象在回收內存時可能會被釋放。   (2)正在使用(inuse)狀態:處於該狀態下的dentry對象的引用計數d_count大於0,且其d_inode指向相關的inode對象。這種dentry對象不能被釋放。   (3)負(negative)狀態:與目錄項相關的inode對象不復存在(相應的磁盤索引節點可能已經被刪除),dentry對象的d_inode指針為NULL。但這種dentry對象仍然保存在dcache中,以便後續對同一文件名的查找能夠快速完成。這種dentry對象在回收內存時將首先被釋放。   Linux為了提高目錄項對象的處理效率,設計與實現了目錄項高速緩存(dentry cache,簡稱dcache),它主要由兩個數據結構組成:   1. 哈希鏈表dentry_hashtable:dcache中的所有dentry對象都通過d_hash指針域鏈到相應的dentry哈希鏈表中。   2. 未使用的dentry對象鏈表dentry_unused:dcache中所有處於“unused”狀態和“negative”狀態的dentry對象都通過其d_lru指針域鏈入dentry_unused鏈表中。該鏈表也稱為LRU鏈表。   目錄項高速緩存dcache是索引節點緩存icache的主控器(master),也即dcache中的dentry對象控制著icache中的inode對象的生命期轉換。無論何時,只要一個目錄項對象存在於dcache中(非negative狀態),則相應的inode就將總是存在,因為inode的引用計數i_count總是大於0。當dcache中的一個dentry被釋放時,針對相應inode對象的iput()方法就會被調用。     1 目錄項對象的SLAB分配器緩存dentry_cache   dcache是建立在dentry對象的slab分配器緩存dentry_cache(按照Linux的命名規則,似乎應該是dentry_cachep,^_^)之上的。因此,目錄項對象的創建和銷毀都應該通過kmem_cache_alloc()函數和kmem_cache_free()函數來進行。   dentry_cache是一個kmem_cache_t類型的指針。它定義在dcache.c文件中:   static kmem_cache_t *dentry_cache;   這個slab分配器緩存是在dcache機制的初始化例程dcache_init()中通過調用函數kmem_cache_create()來創建的。     1.1 分配接口   dcache在kmem_cache_alloc()的基礎上定義兩個高層分配接口:d_alloc()函數和d_alloc_root()函數,用來從dentry_cache slab分配器緩存中為一般的目錄項和根目錄分配一個dentry對象。   其中,d_alloc()的實現如下:   #define NAME_ALLOC_LEN(len) ((len+16) & ~15)   strUCt dentry * d_alloc(struct dentry * parent, const struct qstr *name)   {   char * str;   struct dentry *dentry;     dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);   if (!dentry)   return NULL;     if (name->len > DNAME_INLINE_LEN-1) {   str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);   if (!str) {   kmem_cache_free(dentry_cache, dentry);   return NULL;   }   } else   str = dentry->d_iname;     memcpy(str, name->name, name->len);   str[name->len] = 0;     atomic_set(&dentry->d_count, 1);   dentry->d_flags = 0;   dentry->d_inode = NULL;   dentry->d_parent = NULL;   dentry->d_sb = NULL;   dentry->d_name.name = str;   dentry->d_name.len = name->len;   dentry->d_name.hash = name->hash;   dentry->d_op = NULL;   dentry->d_fsdata = NULL;   INIT_LIST_HEAD(&dentry->d_vfsmnt);   INIT_LIST_HEAD(&dentry->d_hash);   INIT_LIST_HEAD(&dentry->d_lru);   INIT_LIST_HEAD(&dentry->d_subdirs);   INIT_LIST_HEAD(&dentry->d_alias);   if (parent) {   dentry->d_parent = dget(parent);   dentry->d_sb = parent->d_sb;   spin_lock(&dcache_lock);   list_add(&dentry->d_child, &parent->d_subdirs);   spin_unlock(&dcache_lock);   } else   INIT_LIST_HEAD(&dentry->d_child);     dentry_stat.nr_dentry++;   return dentry;   }   NOTE:   (1)如果文件名的長度大於15,則調用kmalloc()函數從slab分配器中為文件名分配內存;否則將文件名拷貝到d_iname數組中,並讓b_name.name指針指向d_iname。   (2)引用計數d_count將被初始化為1,其余成員都被初始化為NULL。   (3)如果父目錄的dentry被給定,則設置d_parent指針指向父目錄的dentry對象(因此必須通過dget函數來增加父目錄dentry對象的引用計數)。並通過d_child指針域將這個dentry對象鏈入父目錄dentry對象的d_subdirs鏈表。否則,將d_child初始化為指向自身。   (4)將dcache統計信息中的dentry對象總數nr_dentry值加1。   函數d_alloc_root()用來為fs的根目錄(並不一定是系統全局文件系統的根“/”)分配dentry對象。它以根目錄的inode對象指針為參數,如下所示:   struct dentry * d_alloc_root(struct inode * root_inode)   {   struct dentry *res = NULL;     if (root_inode) {   res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });   if (res) {   res->d_sb = root_inode->i_sb;   res->d_parent = res;   d_instantiate(res, root_inode);   }   }   return res;   }   (1)可以看出,首先還是必須調用d_alloc()函數來從dentry_cache slab分配器緩存中分配一個dentry對象。注意!特別之處在於d_alloc()函數的調用參數。   (2)然後,將所分配的dentry對象的d_parent指針設置為指向自身。NOTE!這一點是判斷一個dentry對象是否是一個fs的根目錄的唯一准則(include/linux/dcache.h):   #define IS_ROOT(x) ((x)==(x)->d_parent)   (3)最後,通過調用d_instantiate()函數來實例化這個dentry對象。   函數d_instantiate用於向dentry結構中填寫inode信息,因此該函數會將一個dentry對象從negative狀態轉變為inuse狀態。如下所示:   void d_instantiate(struct dentry *entry, struct inode * inode)   {   spin_lock(&dcache_lock);   if (inode)   list_add(&entry->d_alias, &inode->i_dentry);   entry->d_inode = inode;   spin_unlock(&dcache_lock);   }   NOTE! 函數d_instantiate()假定在被調用之前,調用者已經增加了inode的引用計數。     1.2 釋放接口   目錄項緩存dcache定義了兩個高層釋放接口:d_free()函數和dentry_iput()函數。其中,d_free函數用來將dcache中不使用的dentry對象釋放回dentry_cache slab分配器緩存;而dentry_iput()函數則用來釋放一個dentry對象對一個inode對象的引用關聯。   函數d_free()首先調用dentry對象操作方法中的d_release()函數(如果定義了的話),通常在d_release()函數中釋放dentry->fsdata數據。然後,用dname_external()函數判斷是否已經為目錄項名字d_name分配了內存,如果是,則調用kfree()函數釋放d_name所占用的內存。接下來,調用kmem_cache_free()函數釋放這個dentry對象。最後,修改dcache統計信息中的dentry對象總數(減1)。其源碼如下:   /* no dcache_lock, please */   static inline void d_free(struct dentry *dentry)   {   if (dentry->d_op && dentry->d_op->d_release)   dentry->d_op->d_release(dentry);   if (dname_external(dentry))   kfree(dentry->d_name.name);   kmem_cache_free(dentry_cache, dentry);   dentry_stat.nr_dentry--;   }   其中,dname_external()是定義在dcache.h頭文件中的內聯函數,如下:   static __inline__ int dname_external(struct dentry *d)   {   return d->d_name.name != d->d_iname;   }   而dentry_iput()函數則主要用於在調用d_free()函數釋放一個dentry對象之前,釋放該dentry對象與相應inode對象的關聯,從而將一個dentry對象轉變為negative狀態。主要包括如下幾項任務:(1)將這個dentry對象從相應inode對象的別名鏈表i_dentry中摘除;(2)解除自旋鎖dcache_lock;(3)調用dentry的操作方法d_iput()函數(如果有的話),或者iput()方法。   該函數與d_instantiate()函數是相反的,如下:   static inline void dentry_iput(struct dentry * dentry)   {   struct inode *inode = dentry->d_inode;   if (inode) {   dentry->d_inode = NULL;   list_del_init(&dentry->d_alias);   spin_unlock(&dcache_lock);   if (dentry->d_op && dentry->d_op->d_iput)   dentry->d_op->d_iput(dentry, inode);   else   iput(inode);   } else   spin_unlock(&dcache_lock);   }   NOTE:   (1)如果定義了dentry方法d_iput(),則dentry_iput()通過調用d_iput()方法來釋放inode對象,否則就通過iput()來釋放inode對象。   (2)dent




Copyright © Linux教程網 All Rights Reserved