在上一节,我们说到journal_init(void)里用到的两个重要函数,一个是journal_init_caches()和jbd2_journal_destroy_caches();
前者的实现在journal.c(2333)
static int __init journal_init_caches(void) { int ret;
ret = jbd2_journal_init_revoke_caches(); if (ret == 0) ret = journal_init_jbd2_journal_head_cache(); if (ret == 0) ret = journal_init_handle_cache(); return ret; }
|
这个函数又分别调用了三个初始化函数,它的实现分别在
revoke.c(202)
int __init jbd2_journal_init_revoke_caches(void) { J_ASSERT(!jbd2_revoke_record_cache); J_ASSERT(!jbd2_revoke_table_cache);
jbd2_revoke_record_cache = kmem_cache_create("jbd2_revoke_record", sizeof(struct jbd2_revoke_record_s), 0, SLAB_HWCACHE_ALIGN|SLAB_TEMPORARY, NULL); if (!jbd2_revoke_record_cache) goto record_cache_failure;
jbd2_revoke_table_cache = kmem_cache_create("jbd2_revoke_table", sizeof(struct jbd2_revoke_table_s), 0, SLAB_TEMPORARY, NULL); if (!jbd2_revoke_table_cache) goto table_cache_failure; return 0; table_cache_failure: jbd2_journal_destroy_revoke_caches(); record_cache_failure: return -ENOMEM; }
|
这个函数的功能就到创建两个cache,通过kmem_cache_create(slab.c/2151)来创建,第一个参数是这个cache的名称,第一个参数是cache的名称,第二个是这个cache里对象的大小,第三个是对齐方式,第四个是分配方式,最后一个是对象的构造函数。
说到这里,就不得不说说linux鼎鼎大名SLAB机制了,这里也不想深入说它,简单来说它的思想是我们如果在系统运行过程中频繁分配和释放一种大小相等的内存块(或称为数据类型,也就是刚才所说的对象)时,我们就可以预先分配好一些,这些内存被分割成所要的内存块那样的大小,当要用的时候就如果某块内存没有被占用,那我们就拿来用,用完了,就还给SLAB系统,而不是释放掉,这样就减少系统在分配和释放内存性能损失。这就好比我们平时在家里吃饭一样,我们不是每吃一碗饭就要去买一个碗来盛饭,吃完后就扔了;而是预先买好几个碗,然后吃饭的时候看看哪个是空的,就拿那个来吃饭。如果你要深入了解SLAB系统,去网搜搜,很多人讲得过。
现在来分析 jbd2_journal_init_revoke_caches(void)
第一、二行就是两个assert,断言jbd2_revoke_record_cache和jbd2_revoke_table_cache是空的,学过C语言的人都知道,当然我所指的不是看过谭浩强大师的那本书的“学过”。
第四行,先创建一个jbd2_revoke_record_cache,如果失败,就直接goto record_cache_failure, 估计Dijkstra大师看到这样的语句会吐血,如果你是第一次看到也会觉得goto用得真是恶心,以后你的就会慢慢习惯了,goto在linux内核中用得很美的。
剩下的两个函数一个在journal.c(1961),
static int journal_init_jbd2_journal_head_cache(void) { int retval;
J_ASSERT(jbd2_journal_head_cache == NULL); jbd2_journal_head_cache = kmem_cache_create("jbd2_journal_head", sizeof(struct journal_head), 0, /* offset */ SLAB_TEMPORARY, /* flags */ NULL); /* ctor */ retval = 0; if (!jbd2_journal_head_cache) { retval = -ENOMEM; printk(KERN_EMERG "JBD: no memory for journal_head cache\n"); } return retval; }
|
另一个在journal.c(2309)
static int __init journal_init_handle_cache(void) { jbd2_handle_cache = kmem_cache_create("jbd2_journal_handle", sizeof(handle_t), 0, /* offset */ SLAB_TEMPORARY, /* flags */ NULL); /* ctor */ if (jbd2_handle_cache == NULL) { printk(KERN_EMERG "JBD: failed to create handle cache\n"); return -ENOMEM; } return 0; }
|
和本节所讲的第一个函数差不多,这里就不啰嗦了,至于他们创建的cache具体做什么用,容我日后再说。
本节的最后讲的是jbd2_journal_destroy_caches(),定义在journal.c(2345)
static void jbd2_journal_destroy_caches(void) { jbd2_journal_destroy_revoke_caches(); jbd2_journal_destroy_jbd2_journal_head_cache(); jbd2_journal_destroy_handle_cache(); }
|
又调用了三个函数,实际上三个函数就实现一个功能,就是把前面所说的函数如果创建了的cache销毁。Take jbd2_journal_destroy_revoke_caches an example.
(revoke.c/190)
void jbd2_journal_destroy_revoke_caches(void) { if (jbd2_revoke_record_cache) { kmem_cache_destroy(jbd2_revoke_record_cache); jbd2_revoke_record_cache = NULL; } if (jbd2_revoke_table_cache) { kmem_cache_destroy(jbd2_revoke_table_cache); jbd2_revoke_table_cache = NULL; } }
|
很简单,就是如果前面相应的cache不为空就销毁之。kmem_cache_destroy这个函数在slab.c(2491)里实现。当然我们是不希望出错的,但在计算机中,我们鲁迅大师早就说过:“我因为常见些但愿不如所料,以为未毕竟如所料的事,却每每恰如所料的起来,所以很恐怕这事也一律。”所以在编程的时候要处处小心。
好了,到这里所有的初始化工作就已经完成。
当然还有一个void __exit journal_exit(void)函数,实际上就做一些清理的工作(删除/proc/fs/jdb2目录,还有把在 journal_init(void)创建的cache销毁)。
这样jbd2模块就建立好了,可以用了。但具体怎么用,请听下回分解。
阅读(4307) | 评论(0) | 转发(4) |