
每个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),它主要由两个数据结构组成:
哈希链表dentry_hashtable:dcache中的所有dentry对象都通过d_hash指针域链到相应的dentry哈希链表中。
未使用的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()方法就会被调用。
struct dentry
{
atomic_t d_count;
unsigned int d_flags;/* protected by d_lock */
spinlock_t d_lock;/* per dentry lock */
struct inode *d_inode;/* Where the name belongs to - NULL is * negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
struct dentry * d_parent; /* parent directory */
struct qstr d_name;//contain the name,length,hash value of this dentry
struct list_head d_lru;/* LRU list */
/*
* d_child and d_rcu can share memory
*/
union
{
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children,all the children are linked together*/
struct list_head d_alias;
/* inode alias list,list of all the dentry share the same inode*/
unsigned long d_time;/* used by d_revalidate */
struct dentry_operations *d_op;
struct super_block * d_sb;
/* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
#ifdef CONfig_PROFILING
struct dcookie_struct *d_cookie; /* cookie,if any */
#endif
int d_mounted;
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};
1.1 分配接口
dcache在kmem_cache_alloc()的基础上定义两个高层分配接口:d_alloc()函数和d_alloc_root()函数,用来从dentry_cache slab分配器缓存中为一般的目录项和根目录分配一个dentry对象。
Dentry本身是一个树形结构,d_alloc和d_alloc_root用于build这棵树:
struct dentry *d_alloc(struct dentry * parent,const struct qstr *name)
函数d_alloc_root()用来为fs的根目录(并不一定是系统全局文件系统的根“/”)分配dentry对象。它以根目录的inode对象指针为参数,如下所示:
struct dentry * d_alloc_root(struct inode * root_inode)
{
struct dentry *res = NULL;
if (root_inode)
{
static const struct qstr name = { .name = "/",.len = 1 };
res = d_alloc(NULL,&name);
if (res)
{
res->d_sb = root_inode->i_sb;
/*
将所分配的dentry对象的d_parent指针设置为指向自身。
NOTE!这一点是判断一个dentry对象是否是一个fs的根目录的唯一准则
(include/linux/dcache.h):#define IS_ROOT(x)((x)==(x)->d_parent)
*/
res->d_parent = res;
d_instantiate(res,root_inode);
}
}
return res;
}
函数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的引用计数。
struct dentry *d_alloc_name(struct dentry *parent,const char *name)
{
struct qstr q;
q.name = name;
q.len = strlen(name);
q.hash = full_name_hash(q.name,q.len);
return d_alloc(parent,&q);
}
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对象总数(