说明:slab.c文件主要实现了memcached的slab分配思想。
一. 程序接口
/** Init the subsystem. 1st argument is the limit on no. of bytes to allocate,
0 if no limit. 2nd argument is the growth factor; each slab will use a chunk
size equal to the previous slab's chunk size times this factor.
3rd argument specifies if the slab allocator should allocate all memory
up front (if true), or allocate memory in chunks as it is needed (if false)
*/
void slabs_init(const size_t limit, const double factor, const bool prealloc);
/**
* Given object size, return id to use when allocating/freeing memory for object
* 0 means error: can't store such a large object
*/
unsigned int slabs_clsid(const size_t size);
/** Allocate object of given length. 0 on error */ /*@null@*/
void *slabs_alloc(const size_t size, unsigned int id);
/** Free previously allocated object */
void slabs_free(void *ptr, size_t size, unsigned int id);
|
其中slab_init是由memcached.c中的main函数在初始化的时候调用的。
二. 源代码注释分析
1. slabclass_t及相关数据结构
typedef struct {
unsigned int size; /* sizes of items */
unsigned int perslab; /* how many items per slab */
void **slots; /* list of item ptrs, 这里是slabclass的所有空闲slots/chunks*/
unsigned int sl_total; /* size of slots,当前空闲losts 数组的大小, */
unsigned int sl_curr; /* first free slot */
void *end_page_ptr; /* pointer to next free item at end of page, or 0 */
unsigned int end_page_free; /* number of items remaining at end of last alloced page */
unsigned int slabs; /* how many slabs were allocated for this class */
void **slab_list; /* array of slab pointers */
unsigned int list_size; /* size of slab_list*/
unsigned int killing; /* index+1 of dying slab, or zero if none */
size_t requested; /* The number of requested bytes */
} slabclass_t;
static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES]; static size_t mem_limit = 0; static size_t mem_malloced = 0; static int power_largest;
//用于管理一次性分配给memcached所有内存时的一些管理变量。 static void *mem_base = NULL; static void *mem_current = NULL; static size_t mem_avail = 0;
/** * Access to the slab allocator is protected by this lock */ static pthread_mutex_t slabs_lock = PTHREAD_MUTEX_INITIALIZER;
注释:
1)slab是每次分配内存的单位,大小1M, page和slab是同一个东西,只不过在不同的地点说法不一样。
2) 每个slabclass_t中的所有的slab的chunk size大小相同。 其实每个chunk存储一个item,所以 item的大小也是chunk的大小,只不过,item是memcached数据管理的基本单位,而chunk是仅仅是存储item的内存块。
3) memcached中只有slabclass,并有对应slab的数据结构,因为不需要。
|
2. slab_init
/* 本函数用于根据一个 item的大小,寻找要存储的slabclass
* 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
*/
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. slab_init
/**
* Determines the chunk sizes and initializes the slab class descriptors
* accordingly.
*/
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; //32+chunk_size(48)=80
mem_limit = limit;
if (prealloc) { //当启动memcached用-L参数时使用。这里是一次分配给memcached所有内存,default is 64MB
/* 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)); //初始化每个slabclass, settings.item_size_max=1MB , factor=1.25
// POWER_LARGEST 200 , 其实只能分配42个slab class.因为经过42次factor, //每个chunksize已经达到1MB了
while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
/* Make sure items are always n-byte aligned */
if (size % CHUNK_ALIGN_BYTES) // CHUNK_ALIGN_BYTES=8Bytes
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
slabclass[i].size = size;
slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
size *= factor;
if (settings.verbose > 1) {
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
}
} //分配出的slabclass[i].size=80,104,136...
power_largest = i; //i=42
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[i].size, slabclass[i].perslab);
}
...
#ifndef DONT_PREALLOC_SLABS
{
char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");
if (pre_alloc == NULL || atoi(pre_alloc) != 0) {
slabs_preallocate(power_largest); //对每个slabclass都分配一个slab
}
}
#endif
}
|
4. slab_preallocate 和 do_slabs_newslab
static void slabs_preallocate (const unsigned int maxslabs) {
int i;
unsigned int prealloc = 0;
// POWER_SMALLEST=1, POWER_LARGEST=200
for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
if (++prealloc > maxslabs)
return;
do_slabs_newslab(i);
}
}
static int do_slabs_newslab(const unsigned int id) {
slabclass_t *p = &slabclass[id];
int len = p->size * p->perslab; //1MB
char *ptr;
if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
(grow_slab_list(id) == 0) || //grow_slab_list是以2的指数增长slabclass的slab_list这个slab指针数组的容量
((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; //当前slab成为最后一个空page
p->end_page_free = p->perslab; //最后一个page的空chunk个数
p->slab_list[p->slabs++] = ptr; //链接该page到slabclass的slab_list上。
mem_malloced += len;
return 1;
}
|
5. slab_alloc
//只是简单地getlock 然后调用do_slabs_alloc
void *slabs_alloc(size_t size, unsigned int id) {
void *ret;
pthread_mutex_lock(&slabs_lock);
ret = do_slabs_alloc(size, id);
pthread_mutex_unlock(&slabs_lock);
return ret;
}
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) { //factor=1.25时,power_largest=42
return NULL;
}
p = &slabclass[id];
assert(p->sl_curr == 0 || ((item *)p->slots[p->sl_curr - 1])->slabs_clsid == 0);
#ifdef USE_SYSTEM_MALLOC
if (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 */
//当最后一个page没有未分配的slots&&并且空闲的slots也没有&&分配新的slabs失败时返回NULL
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) { //有空闲的slots
/* return off our freelist */
ret = p->slots[--p->sl_curr]; //返回第一个空闲的slots
} else { //p->end_page_ptr !=0, 最后一个page还有空闲的slots/item
/* 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;
}
|
6. alloc_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);
}
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_MALLOC
mem_malloced -= size;
free(ptr);
return;
#endif
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; //归还slots到freelist
p->requested -= size;
return;
}
|
7. 总结:
slab allocator(slabclass)是通过slots数组(free list)和end_page_ptr来管理chunk(item)的分配和释放的。
slots数组是空闲的chunk数组,这些空闲的slots都是原来已经分配给其他的item使用,并在一段时间后并释放归还的。就是alloc并free后回收的chunk。sl_curr是空闲的slot数目,而slots数组是以初始16,然后每次以2的倍数增长的,所以,sl_total就是slots数组的长度。但是,slots数组里并不是完全都是空闲slot,只是用到了slots[0]~slots[sl_curr-1]这sl_curr个位置。sl_curr也是当前第一个空闲slot指针的下标(从数组最后开始算。。。)
在分配item(chunk)空间时,优先利用回收的空闲 slots, 如果没有再去最后一个slab中按顺序进行分配。
阅读(1196) | 评论(2) | 转发(0) |