上一篇博客详细分析了GlusterFS之内存池的实现技术,今天我们看看GlusterFS是怎么使用这个技术的。
第一步:分配和初始化:
cli进程在初始化的过程中会涉及到内存池的建立和初始化,具体涉及到内存池初始化的代码如下(在cli.c文件中的glusterfs_ctx_defaults_init函数):
[cpp]
- /* frame_mem_pool size 112 * 64 */
- pool->frame_mem_pool = mem_pool_new (call_frame_t, 32);//为调用针对象分配内存池对象,对象类型是call_frame_t,32个这样的内存块
- if (!pool->frame_mem_pool)
- return -1;
-
- /* stack_mem_pool size 256 * 128 */
- pool->stack_mem_pool = mem_pool_new (call_stack_t, 16);//为调用堆栈对象分配内存池对象,对象类型是call_stack_t,16个这样的内存块
-
- if (!pool->stack_mem_pool)
- return -1;
-
- ctx->stub_mem_pool = mem_pool_new (call_stub_t, 16);
- if (!ctx->stub_mem_pool)
- return -1;
-
- ctx->dict_pool = mem_pool_new (dict_t, 32);
- if (!ctx->dict_pool)
- return -1;
-
- ctx->dict_pair_pool = mem_pool_new (data_pair_t, 512);
- if (!ctx->dict_pair_pool)
- return -1;
-
- ctx->dict_data_pool = mem_pool_new (data_t, 512);
- if (!ctx->dict_data_pool)
- return -1;
由上面的代码可以看出:集合系统中各种结构体对象可能实际会用到的数量来预先分配好,真正需要为对象内存的时候直接从这些内存池中取就可以了,用完之后又放回内存池,这样减少了分配和释放内存的额外系统开销,分配内存往往需要从用户态到内核态切换,这些都是很耗时间的,当然相同的对象还减少了初始化的时间。
代码分配内存调用的函数是mem_pool_new,而不是在上一篇博客结束的mem_pool_new_fn函数,那是因为mem_pool_new是定义的宏函数,就是调用mem_pool_new_fn函数,函数参数分别表示对象所占内存大小、数量和名称(为分配的内存起一个名字,就是对象的名称);
[cpp]
- #define mem_pool_new(type,count) mem_pool_new_fn (sizeof(type), count, #type)
第二步:从内存池中取出一个对象内存块:
如下面代码取出一个调用存根的对象内存块(call_stub_t):
[cpp]
- call_stub_t *new = NULL;
-
- GF_VALIDATE_OR_GOTO (“call-stub”, frame, out);
-
- new = mem_get0 (frame->this->ctx->stub_mem_pool);//从内存池中拿出一个对象内存块
同样使用的函数不是我们介绍的mem_get,而是mem_get0函数,mem-get0封装了mem_get,做参数判断并且把需要使用的内存初始化为0,代码如下:
[cpp]
- void*
- mem_get0 (struct mem_pool *mem_pool)
- {
- void *ptr = NULL;
-
- if (!mem_pool) {
- gf_log_callingfn (“mem-pool”, GF_LOG_ERROR, “invalid argument”);
- return NULL;
- }
-
- ptr = mem_get(mem_pool);//得到一个内存对象块
-
- if (ptr)
- memset(ptr, 0, mem_pool->real_sizeof_type);//初始化0
-
- return ptr;
- }
第三步:放回对象内存块到内存池中:
当我们使用完一个对象以后就会重新放回内存池中,例如还是以调用存根对象(call_stub_t)
[cpp]
- void
- call_stub_destroy (call_stub_t *stub)
- {
- GF_VALIDATE_OR_GOTO (“call-stub”, stub, out);
-
- if (stub->wind) {
- call_stub_destroy_wind (stub);
- } else {
- call_stub_destroy_unwind (stub);
- }
-
- stub->stub_mem_pool = NULL;
- mem_put (stub);//放回对象内存块到内存池中
- out:
- return;
- }
第四步:销毁内存池:
如果整个内存池对象都不需要了,那么销毁掉这个内存池,实现这个功能的函数是mem_pool_destroy:
[cpp]
- void
- mem_pool_destroy (struct mem_pool *pool)
- {
- if (!pool)
- return;
-
- gf_log (THIS->name, GF_LOG_INFO, “size=%lu max=%d total=%”PRIu64,
- pool->padded_sizeof_type, pool->max_alloc, pool->alloc_count);
-
- list_del (&pool->global_list);//从全局内存池对象中拖链
-
- LOCK_DESTROY (&pool->lock);//销毁锁
- GF_FREE (pool->name);//释放名字占用的内存
- GF_FREE (pool->pool);//释放内存池分配的内存,就是提供给用户使用的那一段内存
- GF_FREE (pool);//释放内存池对象占用的内存
-
- return;
- }
一般情况下内存池对象会在程序退出的时候才会释放和销毁,还有一种情况是临时分配的内存池也有可能在系统运行期间释放和销毁,因为不能保证一个预先分配的内存池就能够满足整个系统运行期间那个对象所需要的内存,可能在每一个阶段这个对象使用特别多,以至于把内存池预先分配的对象内存块使用完了,这时就需要临时分配内存池对象,过了这一段时间可能这个对象需要的个数就减少了,这时就需要释放掉临时分配的,已还给系统内存。
OK!内存池管理技术是提供内存使用率和效率的重要手段,Glusterfs使用的内存池技术采用的是Linux内核管理小内存块的分配算法slab,就是基于对象分配内存的技术。可以先去熟悉slab的原理,就能更好的理解Glusterfs的内存池技术了!