分享
 
 
 

Linux2.4目录项缓存dcache机制的实现分析

王朝system·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

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 目录项对象的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 分配接口

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.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)dent

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有