GlusterFS之内存池(mem-pool)使用实例分析

上一篇博客详细分析了GlusterFS之内存池的实现技术,今天我们看看GlusterFS是怎么使用这个技术的。

第一步:分配和初始化:

cli进程在初始化的过程中会涉及到内存池的建立和初始化,具体涉及到内存池初始化的代码如下(在cli.c文件中的glusterfs_ctx_defaults_init函数):

[cpp]

  1. /* frame_mem_pool size 112 * 64 */
  2. pool->frame_mem_pool = mem_pool_new (call_frame_t, 32);//为调用针对象分配内存池对象,对象类型是call_frame_t,32个这样的内存块
  3. if (!pool->frame_mem_pool)  
  4. return -1;  
  5. /* stack_mem_pool size 256 * 128 */
  6. pool->stack_mem_pool = mem_pool_new (call_stack_t, 16);//为调用堆栈对象分配内存池对象,对象类型是call_stack_t,16个这样的内存块
  7. if (!pool->stack_mem_pool)  
  8. return -1;  
  9. ctx->stub_mem_pool = mem_pool_new (call_stub_t, 16);  
  10. if (!ctx->stub_mem_pool)  
  11. return -1;  
  12. ctx->dict_pool = mem_pool_new (dict_t, 32);  
  13. if (!ctx->dict_pool)  
  14. return -1;  
  15. ctx->dict_pair_pool = mem_pool_new (data_pair_t, 512);  
  16. if (!ctx->dict_pair_pool)  
  17. return -1;  
  18. ctx->dict_data_pool = mem_pool_new (data_t, 512);  
  19. if (!ctx->dict_data_pool)  
  20. return -1;  

由上面的代码可以看出:集合系统中各种结构体对象可能实际会用到的数量来预先分配好,真正需要为对象内存的时候直接从这些内存池中取就可以了,用完之后又放回内存池,这样减少了分配和释放内存的额外系统开销,分配内存往往需要从用户态到内核态切换,这些都是很耗时间的,当然相同的对象还减少了初始化的时间。

代码分配内存调用的函数是mem_pool_new,而不是在上一篇博客结束的mem_pool_new_fn函数,那是因为mem_pool_new是定义的宏函数,就是调用mem_pool_new_fn函数,函数参数分别表示对象所占内存大小、数量和名称(为分配的内存起一个名字,就是对象的名称);

[cpp]

  1. #define mem_pool_new(type,count) mem_pool_new_fn (sizeof(type), count, #type)

第二步:从内存池中取出一个对象内存块:

如下面代码取出一个调用存根的对象内存块(call_stub_t):

[cpp]

  1. call_stub_t *new = NULL;  
  2. GF_VALIDATE_OR_GOTO (“call-stub”, frame, out);  
  3. new = mem_get0 (frame->this->ctx->stub_mem_pool);//从内存池中拿出一个对象内存块

同样使用的函数不是我们介绍的mem_get,而是mem_get0函数,mem-get0封装了mem_get,做参数判断并且把需要使用的内存初始化为0,代码如下:

[cpp]

  1. void*  
  2. mem_get0 (struct mem_pool *mem_pool)  
  3. {  
  4. void             *ptr = NULL;  
  5. if (!mem_pool) {  
  6.                 gf_log_callingfn (“mem-pool”, GF_LOG_ERROR, “invalid argument”);  
  7. return NULL;  
  8.         }  
  9.         ptr = mem_get(mem_pool);//得到一个内存对象块
  10. if (ptr)  
  11.                 memset(ptr, 0, mem_pool->real_sizeof_type);//初始化0
  12. return ptr;  
  13. }  

第三步:放回对象内存块到内存池中:

当我们使用完一个对象以后就会重新放回内存池中,例如还是以调用存根对象(call_stub_t)

[cpp]

  1. void
  2. call_stub_destroy (call_stub_t *stub)  
  3. {  
  4.         GF_VALIDATE_OR_GOTO (“call-stub”, stub, out);  
  5. if (stub->wind) {  
  6.                 call_stub_destroy_wind (stub);  
  7.         } else {  
  8.                 call_stub_destroy_unwind (stub);  
  9.         }  
  10.         stub->stub_mem_pool = NULL;  
  11.         mem_put (stub);//放回对象内存块到内存池中
  12. out:  
  13. return;  
  14. }  

第四步:销毁内存池:

如果整个内存池对象都不需要了,那么销毁掉这个内存池,实现这个功能的函数是mem_pool_destroy:

[cpp]

  1. void
  2. mem_pool_destroy (struct mem_pool *pool)  
  3. {  
  4. if (!pool)  
  5. return;  
  6.         gf_log (THIS->name, GF_LOG_INFO, “size=%lu max=%d total=%”PRIu64,  
  7.                 pool->padded_sizeof_type, pool->max_alloc, pool->alloc_count);  
  8.         list_del (&pool->global_list);//从全局内存池对象中拖链
  9.         LOCK_DESTROY (&pool->lock);//销毁锁
  10.         GF_FREE (pool->name);//释放名字占用的内存
  11.         GF_FREE (pool->pool);//释放内存池分配的内存,就是提供给用户使用的那一段内存
  12.         GF_FREE (pool);//释放内存池对象占用的内存
  13. return;  
  14. }  

一般情况下内存池对象会在程序退出的时候才会释放和销毁,还有一种情况是临时分配的内存池也有可能在系统运行期间释放和销毁,因为不能保证一个预先分配的内存池就能够满足整个系统运行期间那个对象所需要的内存,可能在每一个阶段这个对象使用特别多,以至于把内存池预先分配的对象内存块使用完了,这时就需要临时分配内存池对象,过了这一段时间可能这个对象需要的个数就减少了,这时就需要释放掉临时分配的,已还给系统内存。

OK!内存池管理技术是提供内存使用率和效率的重要手段,Glusterfs使用的内存池技术采用的是Linux内核管理小内存块的分配算法slab,就是基于对象分配内存的技术。可以先去熟悉slab的原理,就能更好的理解Glusterfs的内存池技术了!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏娱乐心理测试

vue 方法回调通知执行下一个方法

在项目中有很多这样的需求,在一个方法执行完成拿到数据后才可以执行下一个方法,这就需要在第一个方法执行完后有个回调函数通知下一个方法可以执行了。

1742
来自专栏JetpropelledSnake

Python学习笔记之Python对象反射、类反射、模块反射

1874
来自专栏CSDN技术头条

用 Webhook+Python+Shell 编写一套 Unix 类系统监控工具

本文来自作者 Alinx 在 GitChat 上分享 「用 Webhook+Python+Shell 编写一套 Unix 类系统监控工具」

2314
来自专栏Janti

Java多线程高并发学习笔记(一)——Thread&Runnable

 进程与线程 首先来看百度百科关于进程的介绍: 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活...

73510
来自专栏用户2442861的专栏

Java类加载器深入探索

林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

791
来自专栏积累沉淀

Python快速学习第十一天--Python多线程

Python中使用线程有三种方式: 方法一:函数式 调用thread模块中的start_new_thread()函数来产生新线程。语法如下: thread...

2279
来自专栏Python小屋

回调函数原理与Python实现

回调函数的定义与普通函数并没有本质的区别,但一般不直接调用,而是作为参数传递给另一个函数,当另一个函数中触发了某个事件、满足了某个条件时就会自动调用回调函数。下...

3558
来自专栏JackeyGao的博客

Django小技巧08: Blank or Null

Django Model API 中提供了blank和null两个参数, 非常容易混淆。当我第一次使用 Django 的时候, 总是不能恰当的使用这两个参数。

683
来自专栏我是攻城师

关于线程可见性一个“诡异”的问题

如果执行上面的代码,大多人可能觉得会死循环,因为这里没有任何的同步策略,比如synchronized,Lock,atomic,volatile等关键字,也就是说...

933
来自专栏hbbliyong

C学习笔记(1)-结构体、预处理与多文件结构程序设计

一、结构体的定义与使用 #include <stdio.h> //定义结构体(类似模板) typedef struct { char name[50]; ...

2844

扫码关注云+社区

领取腾讯云代金券