![]() |
会员注册 会员登陆 取回密码 |
|
| 欢迎您回来 | ||
|
目录项缓存dcache
分类:: Unix/Linux / 发表时间 :: 2005-07-13 19:13:15 作者 :: 16hot | 人气 :: | 评论数目 (0) | 发送 | 来源 :: By 阿开 1. 目录项缓存dcache Author:詹荣开 Linux用数据结构dentry来描述fs中与某个文件索引节点相链接的一个目录项(可以是文件,也可以是目录)。 每个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()方法就会被调用。 1.1 目录项对象的SLAB分配器缓存dentry_cache dcache是建立在dentry对象的slab分配器缓存 dentry_cache(按照Linux的命名规则,似乎应该是 dentry_cachep,^_^)之上的。 因此,目录项对象的创建和销毁都应该通过kmem_cache_alloc()函数和kmem_cache_free()函数来进行。 dentry_cache是一个kmem_cache_t类型的指针。它定义在dcache.c文件中: static kmem_cache_t *dentry_cache; 这个slab分配器缓存是在dcache机制的初始化例程dcache_init()中通过调用函数kmem_cache_create()来创建的。 1.1.1 分配接口 dcache在kmem_cache_alloc()的基础上定义两个高层分配接口:d_alloc()函数和d_alloc_root()函数,用来从dentry_cache slab分配器缓存中为一般的目录项和根目录分配一个dentry对象。 其中,d_alloc()的实现如下: #define NAME_ALLOC_LEN(len) ((len+16) & ~15) struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) { char * str; struct dentry *dentry; dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); if (!dentry) return NULL; if (name->len > DNAME_INLINE_LEN-1) { str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL); if (!str) { kmem_cache_free(dentry_cache, dentry); return NULL; } } else str = dentry->d_iname; memcpy(str, name->name, name->len); str[name->len] = 0; atomic_set(&dentry->d_count, 1); dentry->d_flags = 0; dentry->d_inode = NULL; dentry->d_parent = NULL; dentry->d_sb = NULL; dentry->d_name.name = str; dentry->d_name.len = name->len; dentry->d_name.hash = name->hash; dentry->d_op = NULL; dentry->d_fsdata = NULL; INIT_LIST_HEAD(&dentry->d_vfsmnt); INIT_LIST_HEAD(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_lru); INIT_LIST_HEAD(&dentry->d_subdirs); INIT_LIST_HEAD(&dentry->d_alias); if (parent) { dentry->d_parent = dget(parent); dentry->d_sb = parent->d_sb; spin_lock(&dcache_lock); list_add(&dentry->d_child, &parent->d_subdirs); spin_unlock(&dcache_lock); } else INIT_LIST_HEAD(&dentry->d_child); dentry_stat.nr_dentry++; return dentry; } NOTE: (1)如果文件名的长度大于15,则调用kmalloc()函数从slab分配器中为文件名分配内存;否则将文件名拷贝到d_iname数组中,并让b_name.name指针指向d_iname。 (2)引用计数d_count将被初始化为1,其余成员都被初始化为NULL。 (3)如果父目录的dentry被给定,则设置d_parent指针指向父目录的dentry对象(因此必须通过dget函数来增加父目录dentry对象的引用计数)。并通过d_child指针域将这个dentry对象链入父目录dentry对象的d_subdirs链表。否则,将d_child初始化为指向自身。 (4)将dcache统计信息中的dentry对象总数nr_dentry值加1。 函数d_alloc_root()用来为fs的根目录(并不一定是系统全局文件系统的根“/”)分配dentry对象。它以根目录的inode对象指针为参数,如下所示: struct dentry * d_alloc_root(struct inode * root_inode) { struct dentry *res = NULL; if (root_inode) { res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); if (res) { res->d_sb = root_inode->i_sb; res->d_parent = res; d_instantiate(res, root_inode); } } return res; } (1)可以看出,首先还是必须调用d_alloc()函数来从dentry_cache slab分配器缓存中分配一个dentry对象。注意!特别之处在于d_alloc()函数的调用参数。 (2)然后,将所分配的dentry对象的d_parent指针设置为指向自身。NOTE!这一点是判断一个dentry对象是否是一个fs的根目录的唯一准则(include/linux/dcache.h): #define IS_ROOT(x) ((x)==(x)->d_parent) (3)最后,通过调用d_instantiate()函数来实例化这个dentry对象。 函数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的引用计数。 1.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)。其源码如下: /* no dcache_lock, please */ static inline void d_free(struct dentry *dentry) { if (dentry->d_op && dentry->d_op->d_release) dentry->d_op->d_release(dentry); if (dname_external(dentry)) kfree(dentry->d_name.name); kmem_cache_free(dentry_cache, dentry); dentry_stat.nr_dentry--; } 其中,dname_external()是定义在dcache.h头文件中的内联函数,如下: static __inline__ int dname_external(struct dentry *d) { return d->d_name.name != d->d_iname; } 而dentry_iput()函数则主要用于在调用d_free()函数释放一个dentry对象之前,释放该dentry对象与相应inode对象的关联,从而将一个dentry对象转变为negative状态。主要包括如下几项任务: (1)将这个dentry对象从相应inode对象的别名链表i_dentry中摘除; (2)解除自旋锁dcache_lock; (3)调用dentry的操作方法d_iput()函数(如果有的话),或者iput()方法。 该函数与d_instantiate()函数是相反的,如下: static inline void dentry_iput(struct dentry * dentry) { struct inode *inode = dentry->d_inode; if (inode) { dentry->d_inode = NULL; list_del_init(&dentry->d_alias); spin_unlock(&dcache_lock); if (dentry->d_op && dentry->d_op->d_iput) dentry->d_op->d_iput(dentry, inode); else iput(inode); } else spin_unlock(&dcache_lock); } NOTE: (1)如果定义了dentry方法d_iput(),则dentry_iput()通过调用d_iput()方法来释放inode对象,否则就通过iput()来释放inode对象。 (2)dentry_iput()函数假定被调用时调用者已经持有了dcache_lock锁。因此它在返回之前对该自旋锁进行解锁。 1.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中。 1.2.1 哈希链表dentry_hashtable dcache中的每个dentry对象都通过其中的d_hash指针域链入哈希链表dentry_hashtable,以便加快对dentry对象的查找速度。哈希链表dentry_hashtable定义在dcache.c文件中,如下: #define D_HASHBITS d_hash_shift #define D_HASHMASK d_hash_mask static unsigned int d_hash_mask; static unsigned int d_hash_shift; static struct list_head *dentry_hashtable; 指针dentry_hashtable定义了dentry哈希链表表头数组的首地址,而d_hash_mask和d_hash_shift的含义与icache中的inode哈希链表的i_hash_mask和i_hash_shift的含义相同,这里就不再解释。 每一个dentry对象都通过其父目录dentry对象的指针和其文件名的哈希值hash来唯一地确定它所属的哈希链表的表头指针,这是通过d_hash函数来完成的: static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash) { hash += (unsigned long) parent / L1_CACHE_BYTES; hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2); 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); } 可以看出,该函数又向下调用partial_name_hash()函数和end_name_hash()函数来完成哈希值的计算工作。 n dcache的初始化 函数dcache_init()完成整个dcache机制的初始化工作,它主要做两件事: (1)哈希链表表头数组的初始化; (2)slab分配器缓存dentry_cache的创建。 该函数的实现思想与icache的初始化函数inode_init()相似,这里就不再详细分析了。如下所示: static void __init dcache_init(unsigned long mempages) { struct list_head *d; unsigned long order; unsigned int nr_hash; int i; /* * 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. * If fragmentation is too bad then the SLAB_HWCACHE_ALIGN * flag could be removed here, to hint to the allocator that * it should not try to get multiple page regions. */ dentry_cache = kmem_cache_create("dentry_cache", sizeof(struct dentry), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!dentry_cache) panic("Cannot create dentry cache"); #if PAGE_SHIFT < 13 mempages >>= (13 - PAGE_SHIFT); #endif mempages *= sizeof(struct list_head); for (order = 0; ((1UL = 0); printk("Dentry-cache hash table entries: %d (order: %ld, %ld bytes) ", nr_hash, order, (PAGE_SIZE d_count)) BUG(); atomic_inc(&dentry->d_count); } return dentry; } 从上述实现可以看出,对于引用那些d_count=0的dentry对象,我们决不应该使用dget()函数,而是应该使用dget_locked()函数。这是因为:引用一个d_count=0的dentry对象,将使该dentry对象从unused状态转变为inuse状态,该dentry状态也必须从LRU链表中脱离,而在操作dcache链表时是必须先持有自旋锁dcache_lock的。函数dget()并不对调用者由任何调用假设,相反,dget_locked()函数则假定调用者在调用它之前已经持有自旋锁dentry_lock。 函数dget_locked()定义在dcache.c中: struct dentry * dget_locked(struct dentry *dentry) { return __dget_locked(dentry); } 可以看出,内部函数__dget_locked()完成实际的工作: /* This should be called _only_ with dcache_lock held */ static inline struct dentry * __dget_locked(struct dentry *dentry) { atomic_inc(&dentry->d_count); if (atomic_read(&dentry->d_count) == 1) { dentry_stat.nr_unused--; list_del(&dentry->d_lru); INIT_LIST_HEAD(&dentry->d_lru) /* make "list_empty()" work */ } return dentry; } 1.3.2 释放接口dput 函数dput()用于释放对一个dentry对象的引用。该函数的核心就是将dentry对象的引用计数d_count减1。如果d_count减1后还不为0,则dput直接返回即可;否则就将该dentry对象放到LRU链表中,或直接释放掉(在该dentry对象未链入哈希链表的情况下)。其源码如下: void dput(struct dentry *dentry) { if (!dentry) return; repeat: if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock)) return; /* dput on a free dentry? */ if (!list_empty(&dentry->d_lru)) BUG(); /* * 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 (list_empty(&dentry->d_hash)) goto kill_it; list_add(&dentry->d_lru, &dentry_unused); dentry_stat.nr_unused++; /* * Update the timestamp */ dentry->d_reftime = jiffies; spin_unlock(&dcache_lock); return; unhash_it: list_del_init(&dentry->d_hash); kill_it: { struct dentry *parent; list_del(&dentry->d_child); /* drops the lock, at that point nobody can reach this dentry */ dentry_iput(dentry); parent = dentry->d_parent; d_free(dentry); if (dentry == parent) return; dentry = parent; goto repeat; } } NOTE: (1)首先判断是否对LRU链表中的一个dentry对象进行dput操作,如果是则报错。 (2)如果定义了dentry操作方法d_delete(),则应该按照d_delete()方法的功能描述首先调用它来删除这个dentry对象。如果d_delete()返回非0值(执行出错),则跳转到unhash_it部分,将这个dentry对象从哈希链表中摘除,并将他释放回slab分配器缓存。 (3)判断这个dentry对象是否链在哈希链表中,如果没有,则跳转到kill_it部分,将这个dentry对象释放掉。 (4)如果这个dentry对象是链在哈希链表中的,则将它移到dentry_unused链表的首部,并将统计信息的nr_unused域加1,最后更新这个dentry对象的最近一次引用时间戳d_reftime,然后就直接返回。 (5)unhash_it部分:将这个dentry对象从哈希链表中摘除。 (6)kill_it部分:这一部分将dentry对象释放回给dentry_cache slab分配器缓存,其过程如下: n 首先,将dentry对象从它的父目录dentry的d_subdirs链表中摘除。 n然后,调用dentry_iput()函数释放其对相应inode对象的引用。 n保存父目录dentry对象的指针,然后调用d_free()函数将这个dentry对象释放回dentry_cache slab分配器缓存。 n如果这个dentry对象的父目录指针指向自身,这说明这个dentry对象就是fs的根目录,因此就可以返回了。否则,就要对父目录dentry对象做一次dput()操作。 1.4 对哈希链表的操作 (1)向哈希链表中增加一个dentry对象 函数d_rehash()实现这一功能,它首先通过d_hash()函数找到这个dentry对象应该挂到哪一个哈希链表中,然后设置d_hash指针。如下所示(dcache.c): void d_rehash(struct dentry * entry) { struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash); spin_lock(&dcache_lock); list_add(&entry->d_hash, list); spin_unlock(&dcache_lock); } (2)从哈希链表中摘除一个dentry对象 函数d_drop()实现这一点,如下所示(dcache.h): static __inline__ void d_drop(struct dentry * dentry) { spin_lock(&dcache_lock); list_del(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_hash); spin_unlock(&dcache_lock); } 头文件dcache.h中还定义了一个函数d_unhashed(),用来测试一个dentry对象是否没有链接在哈希链表中,如下: static __inline__ int d_unhashed(struct dentry *dentry) { return list_empty(&dentry->d_hash); } 1.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机制实现。 1.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操作。 该函数的源码如下: void prune_dcache(int count) { spin_lock(&dcache_lock); for (;;) { struct dentry *dentry; struct list_head *tmp; tmp = dentry_unused.prev; if (tmp == &dentry_unused) break; list_del_init(tmp); dentry = list_entry(tmp, struct dentry, d_lru); /* If the dentry was recently referenced, don't free it. */ if (dentry->d_flags & DCACHE_REFERENCED) { dentry->d_flags &= ~DCACHE_REFERENCED; list_add(&dentry->d_lru, &dentry_unused); count--; continue; } dentry_stat.nr_unused--; /* Unused dentry with a count? */ if (atomic_read(&dentry->d_count)) BUG(); prune_one_dentry(dentry); if (!--count) break; } spin_unlock(&dcache_lock); } 1.5.2 prune_dcache()函数 该函数用于实现从LRU链表的尾部开始倒序释放指定个数的dentry对象。它从尾部开始扫描LRU链表,如果被扫描的dentry对象设置了DCACHE_REFERENCED标志,则让其继续留在LRU链表中,否则就将其从LRU链表中摘除,然后调用prune_one_dentry()函数释放该dentry对象。其源码如下(dcache.c): void prune_dcache(int count) { spin_lock(&dcache_lock); for (;;) { struct dentry *dentry; struct list_head *tmp; tmp = dentry_unused.prev; if (tmp == &dentry_unused) break; list_del_init(tmp); dentry = list_entry(tmp, struct dentry, d_lru); /* If the dentry was recently referenced, don't free it. */ if (dentry->d_flags & DCACHE_REFERENCED) { dentry->d_flags &= ~DCACHE_REFERENCED; list_add(&dentry->d_lru, &dentry_unused); count--; continue; } dentry_stat.nr_unused--; /* Unused dentry with a count? */ if (atomic_read(&dentry->d_count)) BUG(); prune_one_dentry(dentry); if (!--count) break; } spin_unlock(&dcache_lock); } 上述两个函数prune_one_dentry()和prune_dcache()是dcache的shrink机制的实现基础。在此基础上,Linux实现了根据指定条件压缩dcache的高层接口函数:①shink_dcache_sb()——根据指定的超级块对象,压缩dcache;②shrink_dcache_parent()——根据指定的父目录dentry对象,压缩dcache;③shrink_dcache_memory()——根据优先级压缩dcache。 1.5.3 shrink_dcache_sb()函数 该函数释放dcache的LRU链表中属于某个特定超级块对象的dentry对象。该函数的实现过程主要是两次遍历dentry_unused链表: ①第一次遍历过程将属于指定超级块对象的dentry对象移到dentry_unused链表的首部。 ②第二次遍历则将属于指定超级块对象、且d_count=0的dentry对象释放掉(通过prune_one_dentry函数)。个人认为,判断条件d_count=0是不必要的,当然偏执一点也没什么不好:) 函数源码如下: void shrink_dcache_sb(struct super_block * sb) { struct list_head *tmp, *next; struct dentry *dentry; /* * Pass one ... move the dentries for the specified * superblock to the most recent end of the unused list. */ spin_lock(&dcache_lock); next = dentry_unused.next; while (next != &dentry_unused) { tmp = next; next = tmp->next; dentry = list_entry(tmp, struct dentry, d_lru); if (dentry->d_sb != sb) continue; list_del(tmp); list_add(tmp, &dentry_unused); } /* * Pass two ... free the dentries for this superblock. */ repeat: next = dentry_unused.next; while (next != &dentry_unused) { tmp = next; next = tmp->next; dentry = list_entry(tmp, struct dentry, d_lru); if (dentry->d_sb != sb) continue; if (atomic_read(&dentry->d_count)) continue; dentry_stat.nr_unused--; list_del(tmp); INIT_LIST_HEAD(tmp); prune_one_dentry(dentry); goto repeat; } spin_unlock(&dcache_lock); } 1.5.4 shrink_dcache_parent()函数 该函数释放LRU链表中属于给定父目录对象的子dentry对象。实现源码如下: void shrink_dcache_parent(struct dentry * parent) { int found; while ((found = select_parent(parent)) != 0) prune_dcache(found); } 可以看出,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对象的个数。源代码如下: static int select_parent(struct dentry * parent) { struct dentry *this_parent = parent; struct list_head *next; int found = 0; spin_lock(&dcache_lock); repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; struct dentry *dentry = list_entry(tmp, struct dentry, d_child); next = tmp->next; if (!atomic_read(&dentry->d_count)) { list_del(&dentry->d_lru); list_add(&dentry->d_lru, dentry_unused.prev); found++; } /* * Descend a level if the d_subdirs list is non-empty. */ if (!list_empty(&dentry->d_subdirs)) { this_parent = dentry; #ifdef DCACHE_DEBUG printk(KERN_DEBUG "select_parent: descending to %s/%s, found=%d ", dentry->d_parent->d_name.name, dentry->d_name.name, found); #endif goto repeat; } } /* * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { next = this_parent->d_child.next; this_parent = this_parent->d_parent; #ifdef DCACHE_DEBUG printk(KERN_DEBUG "select_parent: ascending to %s/%s, found=%d ", this_parent->d_parent->d_name.name, this_parent->d_name.name, found); #endif goto resume; } spin_unlock(&dcache_lock); return found; } 这是一个算法比较复杂的函数,对他的NOTE如下: (1)由于所有在以parent为根的部分目录树中的目录项都是parent目录的子目录项(直接或间接),而且dentry对象的父、子关系是级联的,因此函数select_parent()必须搜索出LRU链表中所有parent目录的直接或间接子目录项。 为此,函数维护一个指针this_parent,表示当前正在对部分目录树(以parent为根)中的那一个中间目录(非叶节点)进行搜索。初始时,this_parent指针指向parent,因此整个搜索过程是从上到下、从左到右的一个树型结构遍历过程。 (2)while循环对当前父目录this_parent的子目录项链表d_subdirs进行搜索。如果链表中dentry对象的d_count=0,则将该dentry对象移到LRU链表的表尾,并将返回值found加1。然后判断这个dentry对象的d_subdirs链表是否为空(也即是否为目录树的中间节点),如果不为空,则让this_parent指针指向这个dentry对象,并跳转回repeat部分,从而对这个dentry对象的子目录项链表开始进行搜索。 (3)如果当前while循环中测试的每一个dentry对象都没有d_subdirs链表,也即this_parent目录的子目录项全部为目录树的叶节点,则在完成对这一层次的搜索后,退出while循环。函数接下来考虑上升搜索层次(直到this_parent=parent),于是它将next指针修改为this_parent->d_child.next(当前父目录的兄弟),然后将this_parent指针修改为this_parent->d_parent,然后跳转到resume部分,于是搜索过程就从上一层次正确地继续。 (4)is_parent=parent的情况下退出while循环则宣告整个搜索过程结束。 1.5.5 shringk_dcache_memory()函数 当我们需要内存,但又不知道具体需要多少时,就可以调用这个函数来压缩dcache所占用的内存。该函数通常被kswapd守护进程所调用。如下: void shrink_dcache_memory(int priority, unsigned int gfp_mask) { int count = 0; /* * Nasty deadlock avoidance. * * ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache-> * prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op-> * put_inode->ext2_discard_prealloc->ext2_free_blocks->lock_super-> * DEADLOCK. * * We should make sure we don't hold the superblock lock over * block allocations, but for now: */ if (!(gfp_mask & __GFP_IO)) return; if (priority) count = dentry_stat.nr_unused / priority; prune_dcache(count); kmem_cache_shrink(dentry_cache); } 优先级参数priority值越大(优先级越低),表明对内存的需要就越不迫切。因此prune_dcache()函数释放的dentry对象个数就越少。 1.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的索引节点。 1.7 小结 由于dentry是一种纯软件数据结构,不存在对应的磁盘数据。因此,与icache机制和buffer cache机制不同,dcache中没有如何同步一个dentry对象的机制。 [ 返回 ]
■ 相关文章
·
让firefox自动调用下载器
(2005-09-07)
· 浅谈linux优化及安全配置 (2005-09-07) · python入门1 (2005-09-07) · 以非超级用户身份安装 mod_perl (2005-09-07) · FC3中的JAVA安装及配置 (2005-09-07) · Perl与MandrakeLinux (2005-09-07) · Apache服务器实现用户验证 (2005-09-07) · 受限制环境安装Perl模块方法 (2005-09-07) · Linux内核研究系列之可执行文件格式 (2005-07-13) · 如何阅读源代码--工具篇 (2005-07-13) · 如何阅读源代码 (2005-07-13) · Linux内核编程风格 (2005-07-13) · Linux网络代码导读v0.2 (2005-07-13) · Linuxinodecache分析 (2005-07-13) · 目录项缓存dcache (2005-07-13) · Linux对I/O端口资源的管理 (2005-07-13) · Linux对ISA总线DMA的实现 (2005-07-13) · 基于i386的Linux实现特点剖析——基础的基础 (2005-07-13) · 基于i386的Linux实现特点剖析——关于中断 (2005-07-13) · 基于i386体系结构的Linux实现特点剖析——内存与进程 (2005-07-13) · ELF可执行联接规范(英汉对照版) (2005-07-13) · Linux内核0.11(0.95)详细注释 (2005-07-13) · ar和nm命令的使用 (2005-07-13) · JIDEv1.7 (2005-07-13) · Rhide-1.4.7 (2005-07-13) · KDevelop1.3 (2005-07-13) · Xwpe1.5.26a (2005-07-13) · XwpeFAQ (2005-07-13) · C-Forge1.6-4 (2005-07-13) · cvs客户端大全 (2005-07-13)
■ 发表评论
■ 相关评论
更多评论...
|
| http://www.isyi.com Copyright © 2002-2005 实易数码. All rights Reserved 版权声明:实易数码是本Blog托管服务提供商。实易数码不承担任何责任,请与Blog使用者联系解决。 粤ICP备05023051号 |