每个目录项对象属于以下四种状态之一:
· 空闲状态::处于该状态的目录项对象不包含有效的信息,还没有被VFS使用。它对应的内存区由slab分配器进行管理。
· 未使用状态:处于该状态的目录项对象当前还没有被内核使用。该对象的引用计数器d_count的值为NULL。但其d_inode域仍然指向相关的索引节点。该目录项对象包含有效的信息,但为了在必要时回收内存,它的内容可能被丢弃。
· 正在使用状态:处于该状态的目录项对象当前正在被内核使用。该对象的引用计数器d_count的值为正数,而其d_inode域指向相关的索引节点对象。该目录项对象包含有效的信息,并且不能被丢弃。
· 负状态:与目录项相关的索引节点不复存在,那是因为相应的磁盘索引节点已被删除。该目录项对象的d_inode域置为NULL,但该对象仍然被保存在目录项高速缓存中,以便后续对同一文件目录名的查找操作能够快速完成,术语“负的”容易使人误解,因为根本不涉及任何负值。
为了最大限度地提高处理这些目录项对象的效率,Linux使用目录项高速缓存,它由两种类型的数据结构组成:
· 处于正在使用、未使用或负状态的目录项对象的集合。(三个链表)
· 一个哈希表:所有的目录项都链到其中,从中能够快速获取与给定的文件名和目录名对应的目录项对象。如果访问的对象不在目录项高速缓存中,哈希函数返回一个空值。
目录项高速缓存的作用也相当于索引节点高速缓存的控制器。内核内存中,目录项可能已经不使用,但与其相关的索引节点并不被丢弃,这是由于目录项高速缓存仍在使用它们,因此,索引节点的i_count域不为空。于是,这些索引节点对象还保存在RAM中,并能够借助相应的目录项快速引用它们。
所有“未使用” 目录项对象都存放在一个“最近最少使用”的双向链表中,该链表按照插入的时间排序。换句话说,最后释放的目录项对象放在链表的首部,所以最近最少使用的目录项对象总是靠近链表的尾部。一旦目录项高速缓存的空间开始变小,内核就从链表的尾部删除元素,使得多数最近经常使用的对象得以保留。LRU链表的首元素和尾元素的地址存放在变量dentry_unused中的next 域和 prev域中。目录项对象的d_lru域包含的指针指向该链表中相邻目录的对象。
每个“正在使用”的目录项对象都被插入一个双向链表中,该链表由相应索引节点对象的i_dentry域所指向(由于每个索引节点可能与若干硬连接关联,所以需要一个链表)。目录项对象的d_alias域存放链表中相邻元素的地址。
当指向相应文件的最后一个硬链接被删除后,一个“正在使用”的目录项对象可能变成“负”状态。在这种情况下,该目录项对象被移到“未使用” 目录项对象组成的LRU链表中。每当内核缩减目录项高速缓存时,“负”状态目录项对象就朝着LRU链表的尾部移动,这样一来,这些对象就逐渐被释放。
哈希表是由dentry_hashtable数组实现的。数组中的每个元素是一个指向链表的指针,这种链表就是把具有相同哈希表值的目录项进行散列而形成的。该数组的长度取决于系统已安装RAM的数量。目录项对象的d_hash域包含指向具有相同hash值的链表中的相邻元素。哈希函数产生的值是由目录及文件名的目录项对象的地址计算出的。