Linux dentry cache学习

每个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()方法就会被调用。

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 分配接口

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;

res->d_parent = res;//将所分配的dentry对象的d_parent指针设置为指向自身。NOTE!这一点是判断一个dentry对象是否是一个fs的根目录的唯一准则(include/linux/dcache.h):#define IS_ROOT(x)((x)==(x)->d_parent)

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);

}

.2 释放接口

目录项缓存dcache定义了两个高层释放接口:d_free()函数和 dentry_iput()函数。其中,d_free函数用来将dcache中不使用的dentry对象释放回dentry_cache slab分配器缓存;而dentry_iput()函数则用来释放一个dentry对象对一个inode对象的引用关联。

)。其源码如下:

D_free和__d_free主要用于Dentry内存释放
/*

* no dcache_lock, please.  The caller must decrement dentry_stat.nr_dentry

* inside dcache_lock.

*/

static void d_free(struct dentry *dentry)

{

if (dentry->d_op && dentry->d_op->d_release)

dentry->d_op->d_release(dentry);//主要用于释放dentry->fsdata数据

/* if dentry was never inserted into hash, immediate free is OK */

if (hlist_unhashed(&dentry->d_hash))

__d_free(dentry);

else

call_rcu(&dentry->d_u.d_rcu, d_callback);

}

static void __d_free(struct dentry *dentry)

{

WARN_ON(!list_empty(&dentry->d_alias));

if (dname_external(dentry))//判断是否为d_name分配了内存

kfree(dentry->d_name.name);

kmem_cache_free(dentry_cache, dentry);

}

而dentry_iput()函数则主要用于在调用d_free()函数释放一个 dentry对象之前,释放该dentry对象与相应inode对象的关联,从而将一个dentry对象转变为negative状态。主要包括如下几项任务:(1)将这个dentry对象从相应inode对象的别名链表i_dentry中摘除;(2)解除自旋锁dcache_lock;(3)调用
dentry的操作方法d_iput()函数(如果有的话),或者iput()方法。

该函数与d_instantiate()函数是相反的,如下:

*

* Release the dentry's inode, using the filesystem

* d_iput() operation if defined.

*/

static void dentry_iput(struct dentry * dentry)//0401

__releases(dentry->d_lock)

__releases(dcache_lock)

{

struct inode *inode = dentry->d_inode;

if (inode) {

dentry->d_inode = NULL;

list_del_init(&dentry->d_alias);

spin_unlock(&dentry->d_lock);

spin_unlock(&dcache_lock);

if (!inode->i_nlink)

fsnotify_inoderemove(inode);

if (dentry->d_op && dentry->d_op->d_iput)

dentry->d_op->d_iput(dentry, inode);

else

iput(inode);

} else {

spin_unlock(&dentry->d_lock);

spin_unlock(&dcache_lock);

}

}

NOTE:

(1)如果定义了dentry方法d_iput(),则dentry_iput()通过调用d_iput()方法来释放inode对象,否则就通过iput()来释放inode对象。

(2)dentry_iput()函数假定被调用时调用者已经持有了dcache_lock锁。因此它在返回之前对该自旋锁进行解锁。

2 dcache数据结构

Linux通过在dentry_cache slab分配器缓存上定义了各种dentry链表来有效地管理目录项对象,从而实现dcache机制。它们包括:

1. dentry对象的哈希链表dentry_hashtable。

2. dentry对象的LRU链表dentry_unused。

3. 每个索引节点对象的别名链表i_dentry。每个非negative状态的dentry对象都通过d_alias指针域链入其对应的inode对象的别名链表i_dentry中。

4. 父目录dentry对象的子目录项(目录或文件)链表d_subdirs。每个dentry对象都通过d_child指针域链入其父目录dentry对象的子目录项链表d_subdirs中。

2.1
哈希链表dentry_hashtable

Dentry_hashtable的初始化是在dcache_init_early或者dcache_init中实现的:

static void __init dcache_init_early(void){

int loop;

/* If hashes are distributed across NUMA nodes, defer

* hash allocation until vmalloc space is available.

*/

if (hashdist)

return;

dentry_hashtable =

alloc_large_system_hash("Dentry cache",

sizeof(struct hlist_head),

dhash_entries,

13,

HASH_EARLY,

&d_hash_shift,

&d_hash_mask,

0);

for (loop = 0; loop < (1 << d_hash_shift); loop++)

INIT_HLIST_HEAD(&dentry_hashtable[loop]);

}

static void __init dcache_init(void)

{

int loop;

/*

* A constructor could be added for stable state like the lists,

* but it is probably not worth it because of the cache nature

* of the dcache.

*/

dentry_cache = KMEM_CACHE(dentry,

SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD);

register_shrinker(&dcache_shrinker);

/* Hash may have been set up in dcache_init_early */

if (!hashdist)

return;

dentry_hashtable =

alloc_large_system_hash("Dentry cache",

sizeof(struct hlist_head),

dhash_entries,

13,

0,

&d_hash_shift,

&d_hash_mask,

0);

for (loop = 0; loop < (1 << d_hash_shift); loop++)

INIT_HLIST_HEAD(&dentry_hashtable[loop]);

}

每一个dentry对象都通过其父目录dentry对象的指针和其文件名的哈希值hash来唯一地确定它所属的哈希链表的表头指针,这是通过d_hash函数来完成的:

static inline struct hlist_head *d_hash(struct dentry *parent,

unsigned long hash)

{

hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;

hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS);

return dentry_hashtable + (hash & D_HASHMASK);

}

每个目录项文件名的哈希值是通过full_name_hash()函数(定义在include/linux/dcache.h文件中)来计算的,如下所示:

/* Compute the hash for a name string. */

static inline unsigned int full_name_hash(const unsigned char *name, unsigned int len){

unsigned long hash = init_name_hash();

while (len--)

hash = partial_name_hash(*name++, hash);

return end_name_hash(hash);

}

2.2 dentry对象的LRU链表

对于那些处于“未使用”状态的dentry对象来说,它们被再次访问的可能性很大。因此,不能将它们立即丢弃,而必须将它们在dcache中保留一段时间。为此,Linux通过LRU链表来有效地管理这些未使用的dentry对象。每一个处于unused状态下的dentry通过其d_lru指针域链入系统全局的LRU链表,表头包含在super_block::s_dentry_lru中。

从某种程度上讲,super_block::s_dentry_lru链表就是处于inuse状态下的dentry对象的直接缓存。当一个dentry不再被使用时,它首先应被移到LRU链表中,而不是直接将其丢弃,因为该dentry对象很可能会再次被引用。

另一方面,由于super_block::s_dentry_lru链表中的目录项对象是未使用的,因此当内存紧张时,应该将其中一些很长时间内未被使用的dentry对象释放掉,以缓解系统的压力。

2.3 dcache链表的保护锁

Linux在dcache.c文件中定义了自旋锁dcache_lock,来实现对dcache链表的互斥访问。也即,任何一段想要访问任何一条dcache链表的代码段,都必须首先持有该自旋锁。其定义如下:

spinlock_t dcache_lock = SPIN_LOCK_UNLOCKED;

2.4 dcache统计信息

Linux在dcache.c文件中定义了全局变量dentry_stat来表示dcache的统计信息,如下:

struct dentry_stat_t {

int nr_dentry;

int nr_unused;

int age_limit;          /* age in seconds */

int want_pages;         /* pages requested by system */

int dummy[2];

};

extern struct dentry_stat_t dentry_stat;

3 dentry访问接口——dget/dput函数

要引用dcache中的任何一个dentry对象,都必须通过应用接口函数dget()或dget_locked()来进行;然后在使用完这个dentry对象后,通过释放引用接口dput()函数来释放对它的引用。

3.1
引用接口

引用函数dget()仅仅简单地增加dentry对象的引用计数器,如下所示(dcache.h):

static inline struct dentry *dget(struct dentry *dentry)

{

if (dentry) {

BUG_ON(!atomic_read(&dentry->d_count));的dentry对象,将使该dentry对象从unused状态转变为inuse状态,该dentry状态也必须从LRU链表中脱离,而在操作dcache链表时是必须先持有自旋锁dcache_lock的。函数dget()并不对调用者由任何调用假设,相反,dget_locked()函数则假定调用者在调用它之前已经持有自旋锁dentry_lock。

atomic_inc(&dentry->d_count);

}

return dentry;

}

/* This should be called _only_ with dcache_lock held */

static inline struct dentry * __dget_locked(struct dentry *dentry)

{

atomic_inc(&dentry->d_count);

dentry_lru_del_init(dentry);

return dentry;

}

struct dentry * dget_locked(struct dentry *dentry)

{

return __dget_locked(dentry);

}

3.2
释放接口dput

函数dput()用于释放对一个dentry对象的引用。该函数的核心就是将
dentry,则dput直接返回即可;否则就将该dentry对象放到LRU链表中,或直接释放掉(在该dentry对象未链入哈希链表的情况下)。其源码如下:

/ * This is dput

*

* This is complicated by the fact that we do not want to put

* dentries that are no longer on any hash chain on the unused

* list: we'd much rather just get rid of them immediately.

*

* However, that implies that we have to traverse the dentry

* tree upwards to the parents which might _also_ now be

* scheduled for deletion (it may have been only waiting for

* its last child to go away).

*

* This tail recursion is done by hand as we don't want to depend

* on the compiler to always get this right (gcc generally doesn't).

* Real recursion would eat up our stack space.

*/

/*

* dput - release a dentry

* @dentry: dentry to release

*

* Release a dentry. This will drop the usage count and if appropriate

* call the dentry unlink method as well as removing it from the queues and

* releasing its resources. If the parent dentries were scheduled for release

* they too may now get deleted.

*

* no dcache lock, please.

*/

void dput(struct dentry *dentry)//d_put函数有点麻烦

{

if (!dentry)

return;

repeat:

if (atomic_read(&dentry->d_count) == 1)

might_sleep();

if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))

return;

spin_lock(&dentry->d_lock);

if (atomic_read(&dentry->d_count)) {

spin_unlock(&dentry->d_lock);

spin_unlock(&dcache_lock);

return;

}

/*

* AV: ->d_delete() is _NOT_ allowed to block now.

*/

if (dentry->d_op && dentry->d_op->d_delete) {

if (dentry->d_op->d_delete(dentry))//(执行出错)

goto unhash_it;

}

/* Unreachable? Get rid of it */

if (d_unhashed(dentry))//不在hash表中

goto kill_it;

if (list_empty(&dentry->d_lru)) {//在hash链表中,则将其移植到lru链表的首部

dentry->d_flags |= DCACHE_REFERENCED;

dentry_lru_add(dentry);

}

spin_unlock(&dentry->d_lock);

spin_unlock(&dcache_lock);

return;

unhash_it:

__d_drop(dentry);//unhash from the dentry hash

kill_it:

/* if dentry was on the d_lru list delete it from there */

dentry_lru_del(dentry);//delete from the d_lru list

dentry = d_kill(dentry);//kill the dentry and return the parent(如果dentry->d_parent指向自身,则代表fs的根目录,于是d_kill返回NULL)

if (dentry)//对parent进行递归处理

goto repeat;

}

/**

* d_kill - kill dentry and return parent

* @dentry: dentry to kill

*

* The dentry must already be unhashed and removed from the LRU.

* If this is the root of the dentry tree, return NULL.

*/

static struct dentry *d_kill(struct dentry *dentry)

__releases(dentry->d_lock)

__releases(dcache_lock){

struct dentry *parent;

list_del(&dentry->d_u.d_child);//从parent的children
list中删除

dentry_stat.nr_dentry--; /* For d_free, below */

/*drops the locks, at that point nobody can reach this dentry */

dentry_iput(dentry);//release the dentry's inode

if (IS_ROOT(dentry))//判断是否满足dentry->d_parent=dentry

parent = NULL;

else

parent = dentry->d_parent;

d_free(dentry);//释放占用的内存空间给dentry cache slab

return parent;

}

4 对哈希链表的操作

(1)向哈希链表中增加一个dentry对象

函数d_rehash()实现这一功能,它首先通过d_hash()函数找到这个dentry对象应该挂到哪一个哈希链表中,然后设置d_hash指针。如下所示(dcache.c):

void d_rehash(struct dentry * entry)

{

spin_lock(&dcache_lock);

spin_lock(&entry->d_lock);

_d_rehash(entry);

spin_unlock(&entry->d_lock);

spin_unlock(&dcache_lock);

}

static void _d_rehash(struct dentry * entry)

{

__d_rehash(entry, d_hash(entry->d_parent, entry->d_name.hash));

}

static void __d_rehash(struct dentry * entry, struct hlist_head *list)

{

entry->d_flags &= ~DCACHE_UNHASHED;

hlist_add_head_rcu(&entry->d_hash, list);

}

static inline struct hlist_head *d_hash(struct dentry *parent,

unsigned long hash)

{

hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;

hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS);

return dentry_hashtable + (hash & D_HASHMASK);

}

(2)从哈希链表中摘除一个dentry对象

函数d_drop()实现这一点,如下所示(dcache.h):

D_drop,__d_drop主要将dentry从相应hash中摘除,VFS
lookup将不会找到该dentry:

/**

* d_drop - drop a dentry

* @dentry: dentry to drop

*

*
d_drop() unhashes the entry from the parent dentry hashes, so that it won't

* be found through a VFS lookup any more. Note that this is different from

* deleting the dentry -
d_delete will try to mark the dentry negative if

* possible, giving a successful _negative_ lookup, while d_drop will

* just make the cache lookup fail.

*

* d_drop() is used mainly for stuff that wants to invalidate a dentry for some

* reason (NFS timeouts or autofs deletes).

*

* __d_drop requires dentry->d_lock.

*/

static inline void __d_drop(struct dentry *dentry)//0401

{

if (!(dentry->d_flags & DCACHE_UNHASHED)) {

dentry->d_flags |= DCACHE_UNHASHED;

hlist_del_rcu(&dentry->d_hash);//hlist_del_rcu

}

}

static inline void d_drop(struct dentry *dentry)//0401

{

spin_lock(&dcache_lock);

spin_lock(&dentry->d_lock);

__d_drop(dentry);

spin_unlock(&dentry->d_lock);

spin_unlock(&dcache_lock);

}

头文件dcache.h中还定义了一个函数d_unhashed(),用来测试一个dentry对象是否没有链接在哈希链表中,如下:

static inline int d_unhashed(struct dentry *dentry)

{

return (dentry->d_flags & DCACHE_UNHASHED);

}

5 对LRU链表的管理与操作

对LRU链表dentry_unused的管理和维护主要体现在两点上:

(1)当哈希链表中的一个dentry对象从inuse状态转变为unused状态时,应该将他插入到LRU链表的首部,具体请参见dput()函数的实现。

(2)当系统内存紧张时,应该释放LRU链表中的一些dentry对象,且通常是释放LRU链表尾部的dentry对象(因为它们是最近最少使用的)。但是也可以根据指定条件释放LRU中特定的dentry对象,因此在这之前要做一个挑选过程,并由这一过程将所选中的dentry对象移到dentry_unused链表的尾部。这一机制也称为dcache的压缩(shrink)机制。

下面将详细分析dcache的shrink机制实现。

5.1 prune_one_dentry()函数

该函数实现从LRU链表中释放一个指定的dentry对象。这是一个静态的内部函数,它通常被别的函数调用。NOTE! Prune_one_dentry()函数假定被调用之前,调用者已经将dentry对象从LRU链表中摘除,并且持有自旋锁dcache_lock。因此,它所要做的事情就是:①将这个dentry对象从哈希链表中摘除;②将这个dentry对象从其父目录对象的d_subdirs链表中摘除;③用
dentry_iput()函数释放对相应inode对象的引用;④用d_free()释放这个dentry对象;⑤对父目录dentry对象做一次
dput操作。

static void prune_one_dentry(struct dentry * dentry)

5.2 prune_dcache()函数

该函数用于实现从LRU链表的尾部开始倒序释放指定个数的dentry对象。它从尾部开始扫描LRU链表,如果被扫描的dentry对象设置了DCACHE_REFERENCED标志,则让其继续留在LRU链表中,否则就将其从LRU链表中摘除,然后调用prune_one_dentry()函数释放该dentry对象。

/*Shrink the dcache. This is done when we need more memory, or simply when we

need to unmount something (at which point we need to unuse all dentries).*/

static void prune_dcache(int count){

spin_lock(&dcache_lock);

spin_lock(&sb_lock);

list_for_each_entry(sb, &super_blocks, s_list) {//针对每一个super_block

if (sb->s_nr_dentry_unused == 0)//# of dentries on lru

continue;

。。。。。。

__shrink_dcache_sb(sb, &w_count,DCACHE_REFERENCED);//针对当前sb执行

。。。。。。

spin_unlock(&sb_lock);

spin_unlock(&dcache_lock);

}

/*Shrink the dentry LRU on a given superblock.*/

static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags){

。。。。。。

prune_one_dentry(dentry);/*Throw
away a dentry - free the inode, dput the parent.  This requires that
the LRU list has already been removed.*/

。。。。。。

}

上述两个函数prune_one_dentry()和prune_dcache()是dcache的shrink机制的实现基础。在此基础上,Linux实现了根据指定条件压缩dcache的高层接口函数:①shink_dcache_sb()——根据指定的超级块对象,压缩dcache;②shrink_dcache_parent()——根据指定的父目录dentry对象,压缩dcache;③shrink_dcache_memory()——根据优先级压缩dcache。

5.3 shrink_dcache_sb()函数

该函数释放dcache的LRU链表中属于某个特定超级块对象的dentry对象。该函数的实现过程主要是两次遍历dentry_unused链表:

①第一次遍历过程将属于指定超级块对象的dentry对象移到dentry_unused链表的首部。

②第二次遍历则将属于指定超级块对象、且d_count=0的dentry对象释放掉(通过prune_one_dentry函数)。

void shrink_dcache_sb(struct super_block * sb)

{

__shrink_dcache_sb(sb, NULL, 0);//见5.2

}

5.4 shrink_dcache_parent()函数

该函数释放LRU链表中属于给定父目录对象的子dentry对象。实现源码如下:

void shrink_dcache_parent(struct dentry * parent)

{

struct super_block *sb = parent->d_sb;

int found;

while ((found = select_parent(parent)) != 0)

__shrink_dcache_sb(sb, &found, 0);

}

可以看出,shrink_dcache_parent()函数首先通过调用 select_parent()函数来从LRU链表中查找父目录parent的子目录对象,并将这些子dentry对象移到LRU链表的尾部,并返回所找到的子dentry对象的个数(这一步是为调用prune_dcache()函数做准备的);然后,调用prune_dcache()函数将LRU链表尾部的子dentry对象释放掉。

函数select_parent()是在dcache.c中实现的内部函数,他根据给定的参数parent,在LRU链表中查找父目录parent的子目录对象,并将这些子dentry对象移到LRU链表的尾部,并返回所找到的子dentry对象的个数。

5.5 shringk_dcache_memory()函数

当我们需要内存,但又不知道具体需要多少时,就可以调用这个函数来压缩dcache所占用的内存。该函数通常被kswapd守护进程所调用。

优先级参数priority值越大(优先级越低),表明对内存的需要就越不迫切。因此prune_dcache()函数释放的dentry对象个数就越少。

6 对dentry对象的VFS操作接口

VFS实现了几个对dcache中的dentry对象的操作函数,下面我们列举一些:

1. d_invalidate()——使一个dcache中的dentry对象无效。该函数的核心就是要将指定的dentry对象从哈希链表中摘除。

2. d_find_alias()——为指定inode对象找到一个位于哈希链表中的、且在该索引节点的别名链表i_dentry中的dentry对象。

3. d_prune_aliases()——释放指定inode对象的别名链表i_dentry中未使用的dentry对象。

4. have_submounts()——查看在参数parent指定的部分目录树中是否至少有一个安装点。

5. d_lookup()——在参数parent指定的父目录中查找名字为name的目录项。

6. d_validate()——验证一个dentry对象的有效性。

7. d_delete()——删除一个dentry对象。实际上是将这个dentry对象转变为negative状态或unused状态。

8. d_move()——移动一个dentry对象。

9. __d_path()——得到一个dentry对象的全路径名。

10. is_subdir()——判断一个dentry对象是否是另一个dentry对象的子孙。

11. find_inode_number()——在父目录dir中,查找是否存在参数name指定的名字的目录项,并返回对应inode的索引节点。

7 小结

由于dentry是一种纯软件数据结构,不存在对应的磁盘数据。因此,与icache机制和buffer cache机制不同,dcache中没有如何同步一个dentry对象的机制。

/*

* When a file is deleted, we have two options:

* - turn this dentry into a negative dentry

* - unhash this dentry and free it.

*

* Usually, we want to just turn this into

* a negative dentry, but if anybody else is

* currently using the dentry or the inode

* we can't do that and we fall back on removing

* it from the hash queues and waiting for

* it to be deleted later when it has no users

*/

/**

* d_delete - delete a dentry

* @dentry: The dentry to delete

*

* Turn the dentry into a negative dentry if possible, otherwise

* remove it from the hash queues so it can be deleted later

*/

void d_delete(struct dentry * dentry)//0401

{

int isdir = 0;

/*

* Are we the only user?

*/

spin_lock(&dcache_lock);

spin_lock(&dentry->d_lock);

isdir = S_ISDIR(dentry->d_inode->i_mode);

if (atomic_read(&dentry->d_count) == 1) {//该情况下,turn
the dentry into a negtive dentry

dentry_iput(dentry);//下面分析

fsnotify_nameremove(dentry, isdir);

return;

}

if (!d_unhashed(dentry))//others used the dentry or inode currently,so just unhash it and waiting for it to be deleted later when no users

__d_drop(dentry);

spin_unlock(&dentry->d_lock);

spin_unlock(&dcache_lock);

fsnotify_nameremove(dentry, isdir);

}

区别下面两类API,一类是struct dentry_operations中的,另一类是关于dentry cachede:

Struct dentry_operations:

--------------------------

This describes how a filesystem can overload the standard dentry operations.
Dentries and the dcache are the domain of the VFS and the individual
filesystem
implementations. Device drivers have no business here. These methods
may be set to NULL, as they are either optional or the VFS uses a
default. As of kernel 2.6.22, the following members are defined:

struct dentry_operations {

int (*d_revalidate)(struct dentry *, struct nameidata *);

int (*d_hash) (struct dentry *, struct qstr *);

int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);

int (*d_delete)(struct dentry *);

void (*d_release)(struct dentry *);

void (*d_iput)(struct dentry *, struct inode *);

char *(*d_dname)(struct dentry *, char *, int);

};

d_revalidate: called when the VFS needs to revalidate a dentry. This is
called whenever a name look-up finds a dentry in the dcache. Most
filesystems leave this as NULL, because all their dentries
in the dcache are valid

d_hash: called when the VFS adds a dentry to the hash table

d_compare: called when a dentry should be compared with another

d_delete:
called when the last reference to a dentry is deleted. This means
no-one is using the dentry, however it is still valid and in the dcache

d_release: called when a dentry is really deallocated

d_iput:
called when a dentry loses its inode (just prior to its being
deallocated). The default when this is NULL is that the VFS calls
iput(). If you define this method, you must call iput() yourself

d_dname:
called when the pathname of a dentry should be generated. Useful for
some pseudo filesystems (sockfs, pipefs, ...) to delay pathname
generation. (Instead of doing it when dentry is created,it's
done only when the path is needed.). Real filesystems probably dont
want to use it, because their dentries are present in global dcache
hash, so their hash should be an invariant. As no lock is held,
d_dname() should not try to modify the dentry itself, unless
appropriate SMP safety is used. CAUTION : d_path() logic is quite
tricky. The correct way to return for example "Hello" is to put it at
the end of the buffer, and returns a pointer to the first char.
dynamic_dname() helper function is provided to take care
of this.

Directory Entry Cache API

--------------------------

There are a number of functions defined which permit a filesystem to manipulate dentries:

dget: open a new handle for an existing dentry (this just increments the usage count)

dput:
close a handle for a dentry (decrements the usage count). If the usage
count drops to 0, the "d_delete" method is called and the dentry is
placed on the unused list if the dentry is still
in its parents hash list. Putting the dentry on the unused list just
means that if the system needs some RAM, it goes through the unused list
of dentries and deallocates them. If the dentry has already been
unhashed and the usage count drops to 0, in this
case the dentry is deallocated after the

"d_delete" method is called

d_drop:
this unhashes a dentry from its parents hash list. A subsequent call to
dput() will deallocate the dentry if its usage count drops to 0

d_delete:
delete a dentry. If there are no other open references to the dentry
then the dentry is turned into a negative dentry (the d_iput() method is
called). If there are other references, then
d_drop() is called instead

d_add: add a dentry to its parents hash list and then calls d_instantiate()

d_instantiate: add a dentry to the alias hash list for the inode and
updates the "d_inode" member. The "i_count" member in the inode
structure should be set/incremented. If the inode pointer is
NULL, the dentry is called a "negative dentry". This function is
commonly called when an inode is created for an existing negative dentry

d_lookup:
look up a dentry given its parent and path name component It looks up
the child of that given name from the dcache hash table. If it is found,
the reference count is incremented and the
dentry is returned. The caller must use d_put() to free the dentry when
it finishes using it.

//Scan `nr' dentries and return the number which remain.

static int shrink_dcache_memory(int nr, gfp_t gfp_mask)---->prune_dcache(nr)

/**

* prune_dcache - shrink the dcache

* @count: number of entries to try to free

*

* Shrink the dcache. This is done when we need more memory, or simply when we

* need to unmount something (at which point we need to unuse all dentries).

*

* This function may fail to free any resources if all the dentries are in use.

*/

static void prune_dcache(int count)

/**

* shrink_dcache_sb - shrink dcache for a superblock

* @sb: superblock

*

* Shrink the dcache for the specified super block. This

* is used to free the dcache before unmounting a file

* system

*/

void shrink_dcache_sb(struct super_block * sb)

/**

* shrink_dcache_parent - prune dcache

* @parent: parent of entries to prune

*

* Prune the dcache to remove unused children of the parent dentry.

*/

void shrink_dcache_parent(struct dentry * parent)

D_add和d_add_unique将dentry加入hash表:

/**

* d_add - add dentry to hash queues

* @entry: dentry to add

* @inode: The inode to attach to this dentry

*

* This adds the entry to the hash queues and initializes @inode.

* The entry was actually filled in earlier during d_alloc().

*/

static inline void d_add(struct dentry *entry, struct inode *inode)

{

d_instantiate(entry, inode);

d_rehash(entry);

}

/**

* d_add_unique - add dentry to hash queues without aliasing

* @entry: dentry to add

* @inode: The inode to attach to this dentry

*

* This adds the entry to the hash queues and initializes @inode.

* The entry was actually filled in earlier during d_alloc().

*/

static inline struct dentry *d_add_unique(struct dentry *entry, struct inode *inode)

{

struct dentry *res;

res = d_instantiate_unique(entry, inode);

d_rehash(res != NULL ? res : entry);

return res;

}

static void dentry_lru_del_init(struct dentry *dentry)

{

       if (likely(!list_empty(&dentry->d_lru))) {

              list_del_init(&dentry->d_lru);

              dentry->d_sb->s_nr_dentry_unused--;

              dentry_stat.nr_unused--;

       }

}

 

这里最后给出一个图,表明dentry
和mnt 所组成的一幅全景图,并且还是一个dentry下面安装多个文件系统的例子:体会下
上面的这句话:
/*从一个dentry需找其父节点的时候,要知道mnt才能在这其中做出选择,
从follow_dot_dot我们能学到这个。。。*/

linux dentry cache 转自:http://blog.csdn.net/denzilxu/article/details/9188003的更多相关文章

  1. Linux下nf_conntrack(最全面)_董明磊-CSDN博客_nf_conntrack https://blog.csdn.net/qq_35299863/article/details/79530732

    Linux下nf_conntrack(最全面)_董明磊-CSDN博客_nf_conntrack https://blog.csdn.net/qq_35299863/article/details/79 ...

  2. 【转】【mysql面试】https://blog.csdn.net/hanfazy/article/details/14520437

    公司招聘MySQL DBA,也面试了10个2年MySQL DBA工作经验的朋友,谈谈自己的心得,欢迎大家指点. 1    2年MySQL DBA经验 其中许多有水分,一看到简历自我介绍,说公司项目的时 ...

  3. 使用C++扩展Python的功能 转自:http://blog.csdn.net/magictong/article/details/8897568#comments

    使用C++扩展Python的功能 环境 VS2005Python2.5.4 Windows7(32位) 简介 长话短说,这里说的扩展Python功能与直接用其它语言写一个动态链接库,然后让Python ...

  4. http://blog.csdn.net/luoshengyang/article/details/6651971

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6651971 在Android系统中,提供了独特 ...

  5. http://blog.csdn.net/zhanglvmeng/article/details/11928469

    本系列主要结合<PHP和MYSQL WEB开发 第四版>,在阅读中提出自己认为比较重要的一些问题,以加深对知识的了解程度. 1.简短.中等以及冗长风格的表单变量 $name; //简短风格 ...

  6. 程序员的沟通之痛https://blog.csdn.net/qq_35230695/article/details/80283720

    个人理解: 一般刚工作的程序员总觉得技术最重要.但是当工作年限超过3年.或者岗位需要涉及汇报.需求对接等就会发现沟通非常重要.也许在大公司还不那么明显,但是在小公司.小团队或者创业,沟通甚至可以说是第 ...

  7. http://blog.csdn.net/u012905422/article/details/53340260

    轉自:http://blog.csdn.net/u012905422/article/details/53340260 对于python2.7版本,很多教程(如http://stackoverflow ...

  8. matplotlib绘图基本用法-转自(http://blog.csdn.net/mao19931004/article/details/51915016)

    本文转载自http://blog.csdn.net/mao19931004/article/details/51915016 <!DOCTYPE html PUBLIC "-//W3C ...

  9. [转帖] select、poll、epoll之间的区别总结[整理] + 知乎大神解答 https://blog.csdn.net/qq546770908/article/details/53082870 不过图都裂了.

    select.poll.epoll之间的区别总结[整理] + 知乎大神解答 2016年11月08日 15:37:15 阅读数:2569 http://www.cnblogs.com/Anker/p/3 ...

随机推荐

  1. 阅读redis源代码的一些体会

    最近在学习redis及阅读redis等程序的源码时,有一些收获,特记录到下面. 1.第一步,阅读源代码借助最好可以跟踪的工具去读,如sourceinsight. 我使用的是windows7环境,又因为 ...

  2. 安装 VS 2015 Update 2 + Windows SDK Tools 1.3.1 + Windows SDK 10586.212 后提示找不到 10586.0 SDK 问题的解决方法

    将 Visual Studio 2015 升级到 Update 2,并安装 Windows SDK Tools 1.3.1 和 Windows SDK 10586.212 后,有可能造成原本已安装的 ...

  3. Spring JdbcTemplate 使用总结

    1.查询Object public Classify queryClassifById(int id){ String sql="select * from t_classify where ...

  4. L1-002 打印沙漏 (20 分)

    L1-002 打印沙漏 (20 分) 方法:清晰思路,纸上写出实例,注意循环使用 本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个“*”,要求按下列格式打印 ***** *** * ** ...

  5. tomcat常用技巧

    1. 修改Tomcat的名称 适用场景: 在测试服务器资源有限或是在本机服务器部署多套应用系统时,由于要启动多个TOMCAT服务,且TOMCAT服务没有用名称去区分,会造成维护使用上存在一定晨读的不方 ...

  6. angular1结合webpack构建工具

    目录结构 webpack.config.js const { resolve } = require('path') const webpack = require('webpack') const ...

  7. Flink -- Java Generics Programming

    Flink uses a lot of generics programming, which is an executor Framework with cluster of executor ha ...

  8. SQL Server ->> 重新创建Assembly和自动重建相关的数据库编程对象(存储过程,函数和触发器)

    在SQL Server中,一旦一个Assembly被其他的数据库编程对象(存储过程,函数和触发器)引用了,这个Assembly就不能被删除.但是问题是,在SQL Server要更新一个Assembly ...

  9. 使用命令行执行.sql文件

    用微软自带的sqlcmd工具,可以导入执行.以SQL Server 2008R版本为例: 第一步:Win+R 键入:cmd 命令,开启命令行工具: 第二步:键入:cd C:\Program Files ...

  10. Servlet 2.5为cookie配置HTTPOnly属性

    cookie的HTTPOnly属性,主要是用来防止JavaScript来读取cookie,默认情况下,JavaScript可以通过document.cookie来读取cookie,这样是很不安全的.通 ...