Chinaunix首页 | 论坛 | 博客
  • 博客访问: 349279
  • 博文数量: 63
  • 博客积分: 1412
  • 博客等级: 中尉
  • 技术积分: 648
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-10 23:07
文章分类

全部博文(63)

文章存档

2012年(42)

2011年(21)

我的朋友

分类: C/C++

2012-04-06 16:36:49

四 源码分析

memcached-1.2.8版本

4.1 do_slabs_newslab

前面说过每个 slabclass 都拥有一些 slab, 当所有 slab 都用完时, memcached 会给它分配一个新的 slab, do_slabs_newslab 就是做这个工作的.

点击(此处)折叠或打开

  1. //为每一个slab分配内存
  2. static int do_slabs_newslab(const unsigned int id) {
  3.     slabclass_t *p = &slabclass[id];
  4. #ifdef ALLOW_SLABS_REASSIGN
  5.     int len = POWER_BLOCK;
  6. #else
  7.     int len = p->size * p->perslab;
  8. #endif
  9.     char *ptr;

  10. //分配内存
  11. //在第一次分配slab内存时,执行memory_allocate()
  12.     if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
  13.         (grow_slab_list(id) == 0) ||
  14.         ((ptr = memory_allocate((size_t)len)) == 0))
  15.     {
  16.         //

  17.         MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
  18.         return 0;
  19.     }

  20.     memset(ptr, 0, (size_t)len);

  21. //指向空闲的item
  22.     p->end_page_ptr = ptr;
  23.     p->end_page_free = p->perslab;

  24. //slab_list指向的是当前class中分配的各个slab
  25.     p->slab_list[p->slabs++] = ptr;
  26.     mem_malloced += len;

  27.     MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);
  28.     return 1;
  29. }

分配一个新的 slab, 必须把该 slab 的首地址安插在 slab_list 数组中, 所以先调用 grow_slab_list 来确保 slab_list数组有足够的容量, 如果容量不足, grow_slab_list 会对 slab_list 扩容. 然后调用 memory_allocate 分配 1M 的内存空间, memory_allocate 从预先分配的内存取下 1M 大小的空闲内存块,作为新的 slab. 最后调整 end_page_ptr, 新分配的 slab 全部都是空闲内存块, 所以 end_page_ptr 指向新 slab 的首地址.这个新 slab 的首地址也被安插在 slab_list 数组中.

4.2 do_slabs_alloc

这个函数从指定的 slabclass, 即 slabclass[id], 分配大小为 size 的内存块供申请者使用.分配的原则是, 优先从 slots 指向的空闲链表中分配, 空闲链表没有, 才从 slab 中分配一个空闲的 chunk.

点击(此处)折叠或打开

  1. /*@null@*/
  2. //分配一个item
  3. void *do_slabs_alloc(const size_t size, unsigned int id) {
  4.     slabclass_t *p;
  5.     void *ret = NULL;

  6. //边界条件判断
  7.     if (id < POWER_SMALLEST || id > power_largest) {
  8.         MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0);
  9.         return NULL;
  10.     }

  11. //指向slab class
  12.     p = &slabclass[id];
  13.     assert(p->sl_curr == 0 || ((item *)p->slots[p->sl_curr - 1])->slabs_clsid == 0);

  14. #ifdef USE_SYSTEM_MALLOC
  15.     if (mem_limit && mem_malloced + size > mem_limit) {
  16.         MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
  17.         return 0;
  18.     }
  19.     mem_malloced += size;
  20.     ret = malloc(size);
  21.     MEMCACHED_SLABS_ALLOCATE(size, id, 0, ret);
  22.     return ret;
  23. #endif

  24.     /* fail unless we have space at the end of a recently allocated page,
  25.        we have something on our freelist, or we could allocate a new page */
  26.        //当没有空间时,返回ret=NULL
  27.     if (! (p->end_page_ptr != 0 || p->sl_curr != 0 ||
  28.            do_slabs_newslab(id) != 0)) {
  29.         /* We don't have more memory available */
  30.         ret = NULL;
  31.     } else if (p->sl_curr != 0) {
  32.         /* return off our freelist */
  33.         ret = p->slots[--p->sl_curr];
  34.     } else {
  35.     //从空闲的slab中分配一个item
  36.         /* if we recently allocated a whole page, return from that */
  37.         assert(p->end_page_ptr != NULL);
  38.     //指向空闲slab
  39.     ret = p->end_page_ptr;
  40.         if (--p->end_page_free != 0) {
  41.             p->end_page_ptr += p->size;
  42.         } else {
  43.             p->end_page_ptr = 0;
  44.         }
  45.     }

  46.     if (ret) {
  47.         MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
  48.     } else {
  49.         MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
  50.     }

  51.     return ret;
  52. }

4.3 do_slabs_free

把 ptr 指向的 item 归还给 slabclass[id]。操作很简单, 把 ptr 指向的 item 挂在 slots 空闲链表的最前面。

点击(此处)折叠或打开

  1. //释放一个item
  2. void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
  3.     slabclass_t *p;

  4.     assert(((item *)ptr)->slabs_clsid == 0);
  5.     assert(id >= POWER_SMALLEST && id <= power_largest);
  6.     if (id < POWER_SMALLEST || id > power_largest)
  7.         return;

  8.     MEMCACHED_SLABS_FREE(size, id, ptr);
  9.     p = &slabclass[id];

  10. #ifdef USE_SYSTEM_MALLOC
  11.     mem_malloced -= size;
  12.     free(ptr);
  13.     return;
  14. #endif

  15.     if (p->sl_curr == p->sl_total) { /* need more space on the free list */
  16.         int new_size = (p->sl_total != 0) ? p->sl_total * 2 : 16; /* 16 is arbitrary */
  17.         void **new_slots = realloc(p->slots, new_size * sizeof(void *));
  18.         if (new_slots == 0)
  19.             return;
  20.         p->slots = new_slots;
  21.         p->sl_total = new_size;
  22.     }
  23.     p->slots[p->sl_curr++] = ptr;
  24.     return;
  25. }

4.4 do_item_alloc

从 slab 系统分配一个空闲 item

点击(此处)折叠或打开

  1. /*@null@*/
  2. //nkey------------key的大小
  3. //nbytes----------value的大小

  4. item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
  5.     uint8_t nsuffix;
  6.     item *it;
  7.     char suffix[40];

  8. //得到该item的大小=sizeof(item) + nkey + *nsuffix + nbytes;
  9.     size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);

  10. //slabs_clsid:返回size大小对应的slabclass索引
  11.     unsigned int id = slabs_clsid(ntotal);
  12.     if (id == 0)
  13.         return 0;

  14. //从slabclass[id]中分配一个size大小的trunk
  15.     it = slabs_alloc(ntotal, id);
  16. //当没有空闲的item时,进行LRU计算
  17.     if (it == 0) {
  18.         int tries = 50;
  19.         item *search;

  20.         /* If requested to not push old items out of cache when memory runs out,
  21.          * we're out of luck at this point...
  22.          */

  23.         if (settings.evict_to_free == 0) {
  24.             itemstats[id].outofmemory++;
  25.             return NULL;
  26.         }

  27.         /*
  28.          * try to get one off the right LRU
  29.          * don't necessariuly unlink the tail because it may be locked: refcount>0
  30.          * search up from tail an item with refcount==0 and unlink it; give up after 50
  31.          * tries
  32.          */

  33.         if (tails[id] == 0) {
  34.             itemstats[id].outofmemory++;
  35.             return NULL;
  36.         }

  37.         for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
  38.             //当发现一个item 当前没有被使用(引用计数refcount 为0)
  39.             //且其 生存时间已经为0 或生存时间大于现在时间,
  40.             //就果断的把它释放掉。并再次调slabs_alloc()
  41.             if (search->refcount == 0) {
  42.                 if (search->exptime == 0 || search->exptime > current_time) {
  43.                     itemstats[id].evicted++;
  44.                     itemstats[id].evicted_time = current_time - search->time;
  45.                     STATS_LOCK();
  46.                     stats.evictions++;
  47.                     STATS_UNLOCK();
  48.                 }
  49.                 do_item_unlink(search);
  50.                 break;
  51.             }
  52.         }
  53.         it = slabs_alloc(ntotal, id);
  54.         if (it == 0) {
  55.             itemstats[id].outofmemory++;
  56.             /* Last ditch effort. There is a very rare bug which causes
  57.              * refcount leaks. We've fixed most of them, but it still happens,
  58.              * and it may happen in the future.
  59.              * We can reasonably assume no item can stay locked for more than
  60.              * three hours, so if we find one in the tail which is that old,
  61.              * free it anyway.
  62.              */
  63.             tries = 50;
  64.             for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
  65.                 if (search->refcount != 0 && search->time + 10800 < current_time) {
  66.                     itemstats[id].tailrepairs++;
  67.                     search->refcount = 0;
  68.                     do_item_unlink(search);
  69.                     break;
  70.                 }
  71.             }
  72.             it = slabs_alloc(ntotal, id);
  73.             if (it == 0) {
  74.                 return NULL;
  75.             }
  76.         }
  77.     }

  78.     assert(it->slabs_clsid == 0);

  79.     it->slabs_clsid = id;

  80.     assert(it != heads[it->slabs_clsid]);

  81.     it->next = it->prev = it->h_next = 0;
  82.     it->refcount = 1; /* the caller will have a reference */
  83.     DEBUG_REFCNT(it, '*');
  84.     it->it_flags = 0;
  85.     it->nkey = nkey;
  86.     it->nbytes = nbytes;
  87.     strcpy(ITEM_key(it), key);
  88.     it->exptime = exptime;
  89.     memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
  90.     it->nsuffix = nsuffix;
  91.     return it;
  92. }

4.5 do_item_link

形成了一个完成的 item 后, 就要把它放入两个数据结构中, 一是 memcached 的哈希表, memcached 运行过程中只有一个哈希表, 二是 item 所在的 slabclass 的 LRU 队列.如 图6 所示, 每个 slabclass 都有一个 LRU 队列。

点击(此处)折叠或打开

  1. //形成了一个完成的 item 后, 就要把它放入两个数据结构中
  2. //一是 memcached 的哈希表,
  3. //二是 item 所在的 slabclass 的 LRU 队列

  4. int do_item_link(item *it) {
  5.     MEMCACHED_ITEM_LINK(ITEM_key(it), it->nbytes);
  6.     assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
  7.     assert(it->nbytes < (1024 * 1024)); /* 1MB max size */
  8.     it->it_flags |= ITEM_LINKED;
  9.     it->time = current_time;

  10.     //加入到hashtable中
  11.     assoc_insert(it);

  12.     STATS_LOCK();
  13.     stats.curr_bytes += ITEM_ntotal(it);
  14.     stats.curr_items += 1;
  15.     stats.total_items += 1;
  16.     STATS_UNLOCK();

  17.     /* Allocate a new CAS ID on link. */
  18.     it->cas_id = get_cas_id();

  19. //加入到LRU队列中
  20. //新元素放入到头部,旧的放入到尾部。
  21.     item_link_q(it);

  22.     return 1;
  23. }

4.6 do_item_unlink

do_item_unlink 与 do_item_unlink 做的工作相反, 把 item 从哈希表和 LRU 队列中删除,而且还释放掉 item 所占的内存 (其实只是把 item 放到空闲链表中).

点击(此处)折叠或打开

  1. void do_item_unlink(item *it) {
  2.     MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nbytes);
  3.     if ((it->it_flags & ITEM_LINKED) != 0) {
  4.         it->it_flags &= ~ITEM_LINKED;
  5.         STATS_LOCK();
  6.         stats.curr_bytes -= ITEM_ntotal(it);
  7.         stats.curr_items -= 1;
  8.         STATS_UNLOCK();
  9. //从hashtable中删除
  10.         assoc_delete(ITEM_key(it), it->nkey);
  11. //从LRU队列中删除
  12.         item_unlink_q(it);
  13.         if (it->refcount == 0) item_free(it);
  14.     }
  15. }

4.7 item_get

根据 key 找对应的 item, 为了加快查找速度, memcached 使用一个哈希表对 key 和 item 所在的内存地址做映射. item_get 直接从哈希表中查找就可以了, 当然找到了还要检查 item 是否超时. 超时了的 item将从哈希表和 LRU 队列中删除掉。

点击(此处)折叠或打开

  1. item *item_get(const char *key, const size_t nkey) {
  2.     return item_get_notedeleted(key, nkey, 0);
  3. }


 


 
阅读(1536) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

edishen082013-09-04 11:49:58

分析的相当好