设为首页 收藏本站
查看: 1115|回复: 0

[经验分享] memcache slabs

[复制链接]

尚未签到

发表于 2015-11-18 08:06:42 | 显示全部楼层 |阅读模式
  memcache 使用slab机制对内存进行分配和管理的。
  
  一 整体结构
DSC0000.gif
  
  把内存分成多个层次,在每个层次中,可以有多个page, 所有层次的page大小基本是一直的(为什么不相同,后面介绍),每个page中又分成相同大小的item,里面有多少个item,取决于page大小和item所在的层次,其中相邻层次直接item大小具有如下关系
  size(item: n+1 )  = size( item: n) * grow_factor   
  其中的grow_factor 是可配置的参数。
  在内存分配时,如果需要大小为k的空间,那么就从第n个层次中得到一个item ,其中n需要满足
  size(item:n-1)  <   k  <=  size(item:n)
  那么从n个层次中的这么多个item中取哪个?所有被取光了怎么办?使用完了释放了怎么办? 这些都会在后面进行详细的介绍
  
  
  二 结构成员
  
  typedef struct {unsigned int size;          /* 每个item大小, sizes of items */unsigned int perslab;       /* 每个page中包含多少个item , how many items per slab */void **slots;               /* 空闲的item指针, list of item ptrs */unsigned int sl_total;      /* 以分配空闲的item 个数, size of previous array */unsigned int sl_curr;       /* 当前空闲的item位置(也就是实际空闲item个数),从后往前的, first free slot */void *end_page_ptr;         /* 指向最后一个页面中空闲的item开始位置, pointer to next free item at end of page, or 0 */unsigned int end_page_free; /* 最后一个页面,item个数, number of items remaining at end of last alloced page */unsigned int slabs;         /* 实际使用slab(page)个数 how many slabs were allocated for this class */void **slab_list;           /* 所有page的指针, array of slab pointers */unsigned int list_size;     /* 已经分配page指针个数,size of prev array */unsigned int killing;       /* index+1 of dying slab, or zero if none */size_t requested;           /* 所有被使用了的内存的大小, The number of requested bytes */} slabclass_t;
  
  三 函数介绍
  
  3.1 slabs_clsid()
  /** Figures out which slab class (chunk size) is required to store an item of* a given size.** Given object size, return id to use when allocating/freeing memory for object* 0 means error: can't store such a large object* 根据大小,返回满足条件的item所在的层次 */unsigned int slabs_clsid(const size_t size) {int res = POWER_SMALLEST;if (size == 0)return 0;while (size > slabclass[res].size)if (res++ == power_largest)     /* won't fit in the biggest slab */return 0;return res;}
  
  3.2 slabs_init
  /*** Determines the chunk sizes and initializes the slab class descriptors* accordingly.* slabs 初始化   1 如果需要预先分配的,那就预先分配内存 2* 对每个类别的slab,初始化该slab中每个item大小,item个数  3* 调用slabs_preallocate 预先分配slabs(给每个层次创建一个page) */void slabs_init(const size_t limit, const double factor, const bool prealloc) {int i = POWER_SMALLEST - 1;unsigned int size = sizeof(item) + settings.chunk_size;mem_limit = limit;if (prealloc) {//预先分配内存,并把开始地址和可使用空间大小记录/* Allocate everything in a big chunk with malloc */mem_base = malloc(mem_limit);if (mem_base != NULL) {mem_current = mem_base;mem_avail = mem_limit;} else {fprintf(stderr, "Warning: Failed to allocate requested memory in"" one large chunk./nWill allocate in smaller chunks/n");}}memset(slabclass, 0, sizeof(slabclass));while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {/* Make sure items are always n-byte aligned *///对齐if (size % CHUNK_ALIGN_BYTES)size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);//每个item大小以及item个数slabclass.size = size;slabclass.perslab = settings.item_size_max / slabclass.size;size *= factor;if (settings.verbose > 1) {fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u/n",i, slabclass.size, slabclass.perslab);}}//最后一个slabpower_largest = i;slabclass[power_largest].size = settings.item_size_max;slabclass[power_largest].perslab = 1;if (settings.verbose > 1) {fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u/n",i, slabclass.size, slabclass.perslab);}/* for the test suite:  faking of how much we've already malloc'd */{char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");if (t_initial_malloc) {mem_malloced = (size_t)atol(t_initial_malloc);}}#ifndef DONT_PREALLOC_SLABS{char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");if (pre_alloc == NULL || atoi(pre_alloc) != 0) {slabs_preallocate(power_largest);}}#endif}
  
  3.3 slabs_preallocate
  //对于每个层次的slab,预先分配一个pagestatic void slabs_preallocate (const unsigned int maxslabs) {int i;unsigned int prealloc = 0;/* pre-allocate a 1MB slab in every size class so people don't getconfused by non-intuitive "SERVER_ERROR out of memory"messages.  this is the most common question on the mailinglist.  if you really don't want this, you can rebuild withoutthese three lines.  */for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {if (++prealloc > maxslabs)return;do_slabs_newslab(i);}}
  
  3.4  grow_slab_list
  /确保page指针空间有空闲空间。如果可使用的空间满了,那么扩大成原来的两倍static int grow_slab_list (const unsigned int id) {slabclass_t *p = &slabclass[id];if (p->slabs == p->list_size) {size_t new_size =  (p->list_size != 0) ? p->list_size * 2 : 16;void *new_list = realloc(p->slab_list, new_size * sizeof(void *));if (new_list == 0) return 0;p->list_size = new_size;p->slab_list = new_list;}return 1;}
  
  
  3.5 do_slabs_newslab
  //分配一个类别id为id的page//1 分配空间 2 最后page赋值,以及该page空闲item个数  3 page数目++//,把page指针指向当前page static int do_slabs_newslab(const unsigned int id) {slabclass_t *p = &slabclass[id];int len = p->size * p->perslab;char *ptr;// 1有内存限制并且超过了限制  2 并且确认page指针空间失败 3 分配一个page的内存空间失败if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||(grow_slab_list(id) == 0) ||((ptr = memory_allocate((size_t)len)) == 0)) {MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);return 0;}memset(ptr, 0, (size_t)len);p->end_page_ptr = ptr;p->end_page_free = p->perslab;p->slab_list[p->slabs++] = ptr;mem_malloced += len;MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);return 1;}
  
  
  3.6  do_slabs_alloc
  //根据id,返回对应类别的一个item空间//按照下面优先级进行分配: 1 首先从空闲的空间(回收的) 2 从最后的page中获取 3//新分配一个page  static void *do_slabs_alloc(const size_t size, unsigned int id) {slabclass_t *p;void *ret = NULL;if (id < POWER_SMALLEST || id > power_largest) {MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0);return NULL;}p = &slabclass[id];assert(p->sl_curr == 0 || ((item *)p->slots[p->sl_curr - 1])->slabs_clsid == 0);#ifdef USE_SYSTEM_MALLOCif (mem_limit && mem_malloced + size > mem_limit) {MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);return 0;}mem_malloced += size;ret = malloc(size);MEMCACHED_SLABS_ALLOCATE(size, id, 0, ret);return ret;#endif/* fail unless we have space at the end of a recently allocated page,we have something on our freelist, or we could allocate a new page */// 1 最后面的页面有空间 2 空闲的地方有空间 3 分配一个新的页面if (! (p->end_page_ptr != 0 || p->sl_curr != 0 ||do_slabs_newslab(id) != 0)) {/* We don't have more memory available */ret = NULL;} else if (p->sl_curr != 0) {/* return off our freelist *///从空闲(回收)地方分配ret = p->slots[--p->sl_curr];} else {/* if we recently allocated a whole page, return from that */assert(p->end_page_ptr != NULL);ret = p->end_page_ptr;if (--p->end_page_free != 0) {p->end_page_ptr = ((caddr_t)p->end_page_ptr) + p->size;} else {p->end_page_ptr = 0;}}if (ret) {p->requested += size;MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);} else {MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);}return ret;}
  
  3.7 do_slabs_free
  //释放一个item空间//把释放的空间放到空闲的slot中,不过有可能出现空闲slot不够的情况,对slot进行扩大static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {slabclass_t *p;assert(((item *)ptr)->slabs_clsid == 0);assert(id >= POWER_SMALLEST && id <= power_largest);if (id < POWER_SMALLEST || id > power_largest)return;MEMCACHED_SLABS_FREE(size, id, ptr);p = &slabclass[id];#ifdef USE_SYSTEM_MALLOCmem_malloced -= size;free(ptr);return;#endif//空闲slot空间不够,进行扩大if (p->sl_curr == p->sl_total) { /* need more space on the free list */int new_size = (p->sl_total != 0) ? p->sl_total * 2 : 16;  /* 16 is arbitrary */void **new_slots = realloc(p->slots, new_size * sizeof(void *));if (new_slots == 0)return;p->slots = new_slots;p->sl_total = new_size;}p->slots[p->sl_curr++] = ptr;p->requested -= size;return;}
  
  四 总结
  
  优点:  通过不同层次的item,以及空闲item维护,可以降低请求过程中内存的分配和释放,提高效率
  
  缺点: 1 每次使用的item可能大于时间需求空间量,导致内存浪费。
  
  
  
  五 参考
  
  http://blogold.iyunv.com/u1/56406/showart_2331017.html     memcache探索之slabs
  
  http://tech.idv2.com/2008/07/11/memcached-002/    理解memcache内存机制
  
  
  
  
             版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-140472-1-1.html 上篇帖子: memcache还是redis 下篇帖子: 关于redis、memcache、mongoDB 的对比
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表