linux dentry cache 转自:http://blog.csdn.net/denzilxu/article/details/9188003


对象都属于下列几种状态之一:

未使用()状态:该对象的引用计数的值为,但其指针仍然指向相关的的索引节点。该目录项仍然包含有效的信息,只是当前没有人引用他。这种对象在回收内存时可能会被释放。

正在使用()状态:处于该状态下的对象的引用计数大于,且其指向相关的对象。这种对象不能被释放。

)负()状态:与目录项相关的对象不复存在(相应的磁盘索引节点可能已经被删除),对象的指针为。但这种对象仍然保存在中,以便后续对同一文件名的查找能够快速完成。这种对象在回收内存时将首先被释放。

为了提高目录项对象的处理效率,设计与实现了目录项高速缓存(,简称),它主要由两个数据结构组成:

哈希链表中的所有对象都通过指针域链到相应的哈希链表中。

未使用的对象链表中所有处于“”状态和“”状态的对象都通过其指针域链入链表中。该链表也称为链表。

是索引节点缓存的主控器(),也即中的对象控制着中的对象的生命期转换。无论何时,只要一个目录项对象存在于中(非状态),则相应的就将总是存在,因为的引用计数总是大于。当中的一个被释放时,针对相应对象的方法就会被调用。

struct inode *d_inode;           /* Where the name belongs to - NULL is * negative */

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 */

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*/

void *d_fsdata;                    /* fs-specific data */

()的基础上定义两个高层分配接口:

本身是一个树形结构,用于这棵树:

       static const struct qstr name = { .name = "/", .len = 1 };

res = d_alloc(NULL, &name);

res->d_sb = root_inode->i_sb;

              res->d_parent = res;//将所分配的对象的指针设置为指向自身。!这一点是判断一个对象是否是一个的根目录的唯一准则():#)(()==()-

              d_instantiate(res, root_inode);

用于向结构中填写信息,因此该函数会将一个对象从状态转变为状态。如下所示:

函数()假定在被调用之前,调用者已经增加了的引用计数。


       dentry->d_op->d_release(dentry);//

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

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

判断是否为分配了内存

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

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

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

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

       else

              iput(inode);

)如果定义了方法,则通过调用方法来释放对象,否则就通过来释放对象。

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

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

对象的哈希链表

对象的链表

每个索引节点对象的别名链表。每个非状态的对象都通过指针域链入其对应的对象的别名链表中。

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

哈希链表

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

dentry_hashtable =

       alloc_large_system_hash("Dentry cache",

dentry_hashtable =

       alloc_large_system_hash("Dentry cache",

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

函数(定义在文件中)来计算的,如下所示:

对象的链表

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

链表就是处于状态下的对象的直接缓存。当一个不再被使用时,它首先应被移到链表中,而不是直接将其丢弃,因为该对象很可能会再次被引用。

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

链表的保护锁

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

统计信息

文件中定义了全局变量来表示的统计信息,如下:

访问接口——函数

中的任何一个对象,都必须通过应用接口函数来进行;然后在使用完这个对象后,通过释放引用接口函数来释放对它的引用。

引用接口

仅仅简单地增加对象的引用计数器,如下所示():

释放接口

用于释放对一个对象的引用。。其源码如下:

* 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.

       if (dentry->d_op->d_delete(dentry))//

       goto unhash_it;

}

if (d_unhashed(dentry))//

       goto kill_it;

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

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

dentry = d_kill(dentry);//kill the dentry and return the parent

if (dentry)//

* d_kill - kill dentry and return parent

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

__releases(dentry->d_lock)

__releases(dcache_lock){

list_del(&dentry->d_u.d_child);//

判断是否满足

对哈希链表的操作

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

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

_d_rehash(entry);

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

()实现这一点,如下所示():

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

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

* just make the cache lookup fail.

* reason (NFS timeouts or autofs deletes).

     __d_drop(dentry);

中还定义了一个函数,用来测试一个对象是否没有链接在哈希链表中,如下:

链表的管理与操作

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

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

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

机制实现。

函数

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

函数

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

针对每一个

针对当前执行

()是机制的实现基础。在此基础上,实现了根据指定条件压缩的高层接口函数:①——根据指定的超级块对象,压缩;②——根据指定的父目录对象,压缩;③——根据优先级压缩

函数

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

对象移到链表的首部。

对象释放掉(通过函数)。

函数

链表中属于给定父目录对象的子对象。实现源码如下:

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

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

函数

所占用的内存。该函数通常被守护进程所调用。

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

对象的操作接口

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

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

——为指定对象找到一个位于哈希链表中的、且在该索引节点的别名链表中的对象。

——释放指定对象的别名链表中未使用的对象。

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

——在参数指定的父目录中查找名字为的目录项。

——验证一个对象的有效性。

——删除一个对象。实际上是将这个对象转变为状态或状态。

——移动一个对象。

——得到一个对象的全路径名。

——判断一个对象是否是另一个对象的子孙。

——在父目录中,查找是否存在参数指定的名字的目录项,并返回对应的索引节点。

小结

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

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

* - turn this dentry into a negative dentry

* - unhash this dentry and free it.

* 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

*/

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

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

dentry_iput(dentry);//

if (!d_unhashed(dentry))

       __d_drop(dentry);

,一类是中的,另一类是关于

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:

* 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.

* Shrink the dcache for the specified super block. This

* is used to free the dcache before unmounting a file

* system

* shrink_dcache_parent - prune dcache

* @parent: parent of entries to prune

*

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

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

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

*/

* d_add_unique - add dentry to hash queues without aliasing

所组成的一幅全景图,并且还是一个下面安装多个文件系统的例子:体会下

相关内容