每个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 dentrystruct 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_PROFILINGstruct dcookie_struct *d_cookie; /* cookie, if any */
#endifint             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 listdentry = 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 inodeif (IS_ROOT(dentry))//判断是否满足dentry->d_parent=dentry{parent = NULL;}else{parent = dentry->d_parent;}d_free(dentry);//释放占用的内存空间给dentry cache slabreturn 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 weneed 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我们能学到这个。。。*/

linux 内核 目录项高速缓存 dentry cache 简介相关推荐

  1. linux文件系统的页高速缓存page cache中的核心数据结构address_space

    address_space对象是文件系统中关于内存中页高速缓存的核心数据结构.这篇博客以address_space对象为切入点,分析文件系统的页高速缓存.(这里大部分都是从原作者那里复制过来的,外加上 ...

  2. Linux 内核 3.3 和 3.4 简介

    https://www.ibm.com/developerworks/cn/linux/l-33linuxkernel/ Linux 内核 3.3 和 3.4 简介 M. Tim Jones, 独立作 ...

  3. 2 Linux内核目录概述

    文章目录 Linux目录简介 1 arch 2 block 3 crypto 4 Documentation 5 drivers 6 firmware 7 fs 8 include 9 init 10 ...

  4. linux体系结构+linux内核结构+linux内核目录结构

    一.linux体系结构 (1)用户空间:用户空间中又包含了,用户的应用程序,C库 (2)内核空间:内核空间包括,系统调用,内核,以及与平台架构相关的代码 Linux内核可以划分为3层,最上面是系统调用 ...

  5. linux 内核 目录文件说明

    本文使用的源代码是Linux kernel 3.9.4. 下载地址:https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/linux-3.9.4. ...

  6. LINUX内核目录文件说明

    内核空间和用户空间 在下载内核前,我们应该讨论一些重要的术语和事实.首先了解一下内核空间和用户空间 内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据.不管是内核空间还是 ...

  7. 【Linux】LINUX内核目录文件说明

    kernel第一级目录: 内核源代码的根目录下包含了以下文件夹 arch block crypto Documentation drivers firmware fs include init ipc ...

  8. 高通linux内核目录,高通 android 源代码以及目标系统目录结构

    下面为高通android源代码结构 build/ – Build 环境建立和makefiles生成4 bionic/ – Android C 库 dalvik/ – Android Java 虚拟机 ...

  9. Linux内核目录结构(2.6版本以上的kernel)

    1.documentation: 没有内核代码,提供文档帮助. 2.arch: arch是architecture的缩写.所有与体系结构相关的代码都在这个目录以 include/asm-*/目录中.L ...

最新文章

  1. pytorch model.train() 开启batchnormalize 和 dropout model.eval() 则会关闭dropout
  2. 《看聊天记录都学不会Python到游戏实战?太菜了吧》(10)无底洞的循环
  3. 重启nginx后丢失nginx.pid的解决方法
  4. Java Garbage Collection基础详解------Java 垃圾回收机制技术详解
  5. C语言解决累加和累乘问题
  6. 2019上海开源峰会炉边会谈纪要
  7. android+6.0中兴v5s,中兴v5s
  8. CentOS部署JavaWeb项目
  9. java班级管理系统代码_基于jsp的班级管理系统-JavaEE实现班级管理系统 - java项目源码...
  10. 单例模式 java 例子_java单例模式实例
  11. 常用4种基础统计图表——饼图、条形图、直方图、折线图
  12. 2022年全国职业院校技能大赛 网络搭建与应用赛项 公开赛卷 (十套合卷)
  13. 什么是数据中心基础设施管理(DCIM)
  14. Python面试题(三)
  15. 【文献翻译】构建网络安全知识库的框架-A Framework to Construct Knowledge Base for Cyber Security
  16. STK加载地图与高清影像图
  17. 算法岗面经总结(星环科技)
  18. STM32F4中的CCM内存说明与使用
  19. 简单的Docker入门
  20. 百度贴吧顶帖,没了发发久依然有办法!

热门文章

  1. DPDK — TestPMD
  2. NanoPi NEO Air使用十一:编写SPI驱动点亮TFT屏幕,ST7789V
  3. 同构多核和异构多核简单介绍
  4. Altium Designer从已有的PCB图中导出封装库
  5. NR 5G QoS模型
  6. Java基础学习总结(20)——基础语法
  7. 面试书上一些题目的整理:O(n)复杂度排序年龄 青蛙跳台阶
  8. Java Collections.sort方法对list集合排序
  9. javascript 值传递与作用域
  10. HDU 1557 权利指数 国家压缩 暴力