每个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.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对象总数(减1)。其源码如下:
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)
{
/*这里可知,对于dentry引用计数为0的dentry决不能使用dget,而应使用dget_locked。
因为:引用一个d_count=0的dentry对象,将使该dentry对象从unused状态转变为inuse状态,
该dentry状态也必须从LRU链表中脱离,而在操作dcache链表时是必须先持有自旋锁dcache_lock的。
函数dget()并不对调用者由任何调用假设,
相反,dget_locked()函数则假定调用者在调用它之前已经持有自旋锁dentry_lock。
*/
BUG_ON(!atomic_read(&dentry->d_count));
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对象的引用计数d_count减1。如果d_count减1后还不为0,则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))//返回值非0(执行出错)
{
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);
}
这里最后给出一个图,表明dentry 和mnt 所组成的一幅全景图,并且还是一个dentry下面安装多个文件系统的例子:体会下
上面的这句话:
/*从一个dentry需找其父节点的时候,要知道mnt才能在这其中做出选择, 从follow_dot_dot我们能学到这个。。。*/