memcached学习之slabs部分
来源:互联网 发布:怎么样注册淘宝网 编辑:程序博客网 时间:2024/05/01 05:03
slabs部分的功能介绍
slabs部分是memcached的数据仓库,主要是负责memcached的内存空间的分配,提取以及释放,同时,实际上对这部分的操作是缓存系统的核心,整体上memcached的大部分代码都是围绕这部分来进行的,包括slabs_maintanance_thread,LRU算法,item的增删改查等等。在这里,我并不打算介绍其他部分,是因为后面会有单独章节来介绍item的行为,以及最最关键的LRU算法。另外有一点需要说明,整个系列都没有介绍slabs的maintanance的线程,这里面的功能主要是针对memcached的一个极端情况来处理的,那就是可能出现某个slabclass占据的内存从来没有数据来存储,而有些slabclass却一直分配不到内存空间,这时候需要将没有数据存储的slabclass内存转移给分配不到内存空间的slabclass,这部分由于时间问题,暂时不涉及。
slabs部分的整体逻辑
内部存储结构图
slabs部分操作介绍
slabs部分和assoc部分有点不同,对于memcached申请的内存来说,不再是增删改查的操作了,因为对于内存来说,只有使用和释放这种说法,不过这里的释放仅仅是说将该item对应的内存原来数据给清空,然后放到空闲列表中去,而不是真正地释放掉,这部分内存仍然是要被memcached保持和使用的,因此主要的操作实际上只有两个,分别是slabs_new和slabs_free操作。
slabs部分的代码分解
自定义类型
typedef struct { //chunk_size, 单个数据块的大小 unsigned int size; /* sizes of items */ //单个slab中包含的item数量,其实也就是chunk的数目 unsigned int perslab; /* how many items per slab */ //item指针的数组 void *slots; /* list of item ptrs */ //当前空闲的item数目 unsigned int sl_curr; /* total free items in list */ //当前层包含slab的数量 unsigned int slabs; /* how many slabs were allocated for this class */ //slab的指针数组 void **slab_list; /* array of slab pointers */ //slab的指针数组的大小 unsigned int list_size; /* size of prev array */ //当前请求的内存大小 size_t requested; /* The number of requested bytes */} slabclass_t;
变量分解
1、全局静态变量slabclass,上述slabclass_t的一个数组,也是memcached的数据仓库中心;
2、整型mem_limit, 设定的memcached使用的内存限制大小;
3、整型mem_malloced,已分配的内存大小;
4、布尔类型mem_limit_reached,默认是false, 这是一个信号,用于当内存限制大小被突破时启动LRU的maintenance线程;
5、整型power_largest,实际使用到的slabclass的最大层级;
6、指针mem_base,分配内存的基地址;
7、指针mem_current, 分配内存当前使用的地址;
8、整型mem_avail,当前分配内存的剩余可用大小;
9、互斥锁slabs_lock,用于进入分配slab的互斥锁;
代码分解
1、slabs_init函数,确定slabclass中每层chunk的大小,初始化slabclass每层的配置,并根据参数确定是否预分配内存;
void slabs_init(const size_t limit, const double factor, const bool prealloc) { int i = POWER_SMALLEST - 1; //初始化的起始层,默认是0 unsigned int size = sizeof(item) + settings.chunk_size; //获取最小层中配置的chunk的大小,为item结构体大小和chunk_size之和 mem_limit = limit; //设置内存限制 if (prealloc) { //如果预分配 /* Allocate everything in a big chunk with malloc */ mem_base = malloc(mem_limit); //一次性开辟mem_limit内存 if (mem_base != NULL) { mem_current = mem_base; //尚未有数据存储,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)); //初始化slabclass //下面是根据配置情况对slabclass中每一层配置对应的数据 while (++i < MAX_NUMBER_OF_SLAB_CLASSES-1 && 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); //不足凑齐 slabclass[i].size = size; //当前slabclass层中每个chunk的大小 slabclass[i].perslab = settings.item_size_max / slabclass[i].size; //每一个slab中chunk的数目 size *= factor; //每一层chunk的大小以factor倍数递增 if (settings.verbose > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, slabclass[i].size, slabclass[i].perslab); } } power_largest = i; //最大层,不一定是声明时的最大 slabclass[power_largest].size = settings.item_size_max; // 1M slabclass[power_largest].perslab = 1; //只有一个chunk if (settings.verbose > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, slabclass[i].size, slabclass[i].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); } } if (prealloc) { slabs_preallocate(power_largest); //前面只申请了内存,并没有分别配置给slabclass里面的slab,调用这个函数实现真正分配内存 }}
2、slabs_preallocate函数,主要用于为slabclass中每一层分配1M的存储空间;
static 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 get confused by non-intuitive "SERVER_ERROR out of memory" messages. this is the most common question on the mailing list. if you really don't want this, you can rebuild without these three lines. */ for (i = POWER_SMALLEST; i < MAX_NUMBER_OF_SLAB_CLASSES; i++) { if (++prealloc > maxslabs) //分配到slabclass的实际最大层数即退出 return; if (do_slabs_newslab(i) == 0) { //为给定的层级执行分配内存,每个slabclass包含 16个slab fprintf(stderr, "Error while preallocating slab memory!\n" "If using -L or other prealloc options, max memory must be " "at least %d megabytes.\n", power_largest); exit(1); } }}
3、do_slabs_newslab函数;
static int do_slabs_newslab(const unsigned int id) { slabclass_t *p = &slabclass[id]; slabclass_t *g = &slabclass[SLAB_GLOBAL_PAGE_POOL]; int len = settings.slab_reassign ? settings.item_size_max : p->size * p->perslab; //计算需要的内存大小 char *ptr; if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0 && g->slabs == 0)) { mem_limit_reached = true; //超过内存限制 MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id); return 0; } //下面的几个函数包含内容较多 //首先是grow_slab_list函数,是在该层slab全部被用或者初始化时扩充slab,初始化时是16个,扩充时是原来的2倍,扩充失败返回0,成功返回1,需要注意的是,这里面并不真正添加内存,添加内存空间主要是下面的两个函数完成 //其次是get_page_from_global_pool函数,这是从全局的内存池中获取一部分内存,这个内存获取失败返回NULL //最后调用memory_allocate函数从之前预分配的内存空间去截取一段内存,注意,如果没有预分配的话,立即申请 if ((grow_slab_list(id) == 0) || (((ptr = get_page_from_global_pool()) == NULL) && ((ptr = memory_allocate((size_t)len)) == 0))) { MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id); return 0; } memset(ptr, 0, (size_t)len); //初始化该部分内存 split_slab_page_into_freelist(ptr, id); //将该内存空间按照当前slabclass中chunk大小去释放情况 p->slab_list[p->slabs++] = ptr; //将该部分内存添加到 slab_list中去 MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id); return 1;}
4、grow_slab_list函数,用于增长slab_list;
static int grow_slab_list (const unsigned int id) { slabclass_t *p = &slabclass[id]; //获取对应层的slabclass的指针 if (p->slabs == p->list_size) { //如果分配的,等于使用的slab数目,重新分配 size_t new_size = (p->list_size != 0) ? p->list_size * 2 : 16; //初始化为16,否则为以前2倍 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;}
5、split_slab_page_into_freelist函数,将某部分内存按照item大小切分并存放到空闲列表中;
static void split_slab_page_into_freelist(char *ptr, const unsigned int id) { slabclass_t *p = &slabclass[id]; int x; for (x = 0; x < p->perslab; x++) { do_slabs_free(ptr, 0, id); //将ptr对应的item清空,并添加到空闲列表slots中 ptr += p->size; }}
6、get_page_from_global_pool函数,从全局内存池中获取内存页;
static void *get_page_from_global_pool(void) { slabclass_t *p = &slabclass[SLAB_GLOBAL_PAGE_POOL]; if (p->slabs < 1) { //已空,返回NULL return NULL; } char *ret = p->slab_list[p->slabs - 1]; p->slabs--; return ret;}
7、do_slabs_alloc,分配一个内存空间给一个item;
static void *do_slabs_alloc(const size_t size, unsigned int id, unsigned int *total_chunks, unsigned int flags) { slabclass_t *p; void *ret = NULL; item *it = 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)->slabs_clsid == 0); if (total_chunks != NULL) { *total_chunks = p->slabs * p->perslab; //计算当前slabclass层包含chunk的数量 } /* 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 */ if (p->sl_curr == 0 && flags != SLABS_ALLOC_NO_NEWPAGE) { //如果当前空闲的item为0,表示已经用完了内存空间 do_slabs_newslab(id); //申请加空间 } if (p->sl_curr != 0) { /* return off our freelist */ it = (item *)p->slots; //从slots中获取一个item p->slots = it->next; if (it->next) it->next->prev = 0; /* Kill flag and initialize refcount here for lock safety in slab * mover's freeness detection. */ it->it_flags &= ~ITEM_SLABBED; it->refcount = 1; //添加引用次数 p->sl_curr--; // 可用item数目减1 ret = (void *)it; } else { ret = NULL; } if (ret) { p->requested += size; //size固定 MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret); } else { MEMCACHED_SLABS_ALLOCATE_FAILED(size, id); } return ret;}
8、do_slabs_free函数,释放当前指针的内存空间,并添加到空闲列表;
static void do_slabs_free(void *ptr, const size_t size, unsigned int id) { slabclass_t *p; item *it; assert(id >= POWER_SMALLEST && id <= power_largest); if (id < POWER_SMALLEST || id > power_largest) return; MEMCACHED_SLABS_FREE(size, id, ptr); p = &slabclass[id]; it = (item *)ptr; it->it_flags = ITEM_SLABBED; it->slabs_clsid = 0; it->prev = 0; it->next = p->slots; if (it->next) it->next->prev = it; p->slots = it; //添加到空闲列表 p->sl_curr++; //可用加1 p->requested -= size; return;}
9、memory_allocate函数,分配size大小内存,可能是从预分配内存中获取;
static void *memory_allocate(size_t size) { void *ret; if (mem_base == NULL) { //没有预分配 /* We are not using a preallocated large memory chunk */ ret = malloc(size); } else { ret = mem_current; if (size > mem_avail) { return NULL; } /* mem_current pointer _must_ be aligned!!! */ if (size % CHUNK_ALIGN_BYTES) { size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); } mem_current = ((char*)mem_current) + size; //预分配中获取 if (size < mem_avail) { mem_avail -= size; } else { mem_avail = 0; } } mem_malloced += size; return ret;}
10、slabs_alloc函数,从slabs中分配内存入口;
void *slabs_alloc(size_t size, unsigned int id, unsigned int *total_chunks, unsigned int flags) { void *ret; pthread_mutex_lock(&slabs_lock); //这里开始加锁,全局,slabs仅此一个,可能有性能问题 ret = do_slabs_alloc(size, id, total_chunks, flags); pthread_mutex_unlock(&slabs_lock); return ret;}
11、slabs_free函数,释放空间;
void slabs_free(void *ptr, size_t size, unsigned int id) { pthread_mutex_lock(&slabs_lock); do_slabs_free(ptr, size, id); pthread_mutex_unlock(&slabs_lock);}
12、slabs_clsid函数,根据待存储数据的大小,找到可以存储数据的slabclass的层;
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;}
- memcached学习之slabs部分
- Memcached Slabs
- memcached学习之assoc部分
- memcached学习之item部分
- memcached内存管理(1) ----------------slabs
- Memcached源码分析 - Memcached源码分析之存储机制Slabs(7)
- memcache源码分析之items,slabs
- NoSQL之【memcached】学习
- memcached学习之总览
- memcache slabs
- memcache slabs .
- Memcached学习笔记 — 第四部分:Memcached Java 客户端-gwhalin(2)-性能测试
- Memcached学习笔记 — 第四部分:Memcached Java 客户端-gwhalin(1)-介绍及使用
- 高并发之Memcached实战第10课-“Memcached Get获取数据”部分代码分享
- 高并发之Memcached实战第10课-“Memcached Get获取数据”部分代码分享2
- memcached的LRU部分
- MemCached学习之:(一)安装运行
- memcached之memcached介绍
- cocos2d-x 3.x学习之JumpAction
- c语言中冒泡排序、插入排序、选择排序算法比较
- tomcat,jetty服务器相关
- V4L2学习笔记
- java.net.NoRouteToHostException: No route to host
- memcached学习之slabs部分
- 【复习】servlet之生命周期
- eclipse中相同变量显示变色设置
- 代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧
- Python Regex
- 3.[do...while]+[switch]+[break]+[continue]
- Saltstack 一键部署zabbix-agent
- ThinkPHP清除Redis缓存
- Akka学习笔记:Actors介绍