Slab 算法的发现是基于内核中内存使用的一些特点:一些需要频繁使用的同样大小数据经常在使用后不久又再次被用到;找到合适大小的内存所消耗的时间远远大于释放内存所需要的时间。所以Slab算法的发明人认为内存对象在使用之后不是立即释放给系统而是将它们用链表之类的数据结构管理起来以备将来使用,频繁分配和释放的内存对象应该用缓存管理起来。
Slab Allocation的原理相当简单。它首先从操作系统申请一大块内存,并将其分割成各种尺寸的块Chunk,并把尺寸相同的块分成组Slab Class。
slab分配器带来的好处
- 通过缓存类似对象数据,内核中频繁的小数据对象的分配不会再消耗过多的时间,同时减少了系统的内存碎片
- slab分配支持常用对象数据的初始化,减少了同类对象数据重复的初始化过程
- slab分配支持硬件缓存对齐和着色,这样不同缓存下的对象数据可以使用同样的硬件缓存行,可以提高系统的性能
源代码实现:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <assert.h>
#define POWER_SMALLEST 3
#define POWER_LARGEST 20
#define POWER_BLOCK 1048576
/* powers-of-2 allocation structures */
typedef struct {
unsigned int size; /* sizes of items */
unsigned int perslab; /* how many items per slab */
void **slots; /* list of item ptrs */
unsigned int sl_total; /* size of previous array */
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 prev array */
unsigned int killing; /* index+1 of dying slab, or zero if none */
} slabclass_t;
static slabclass_t slabclass[POWER_LARGEST+1];
static unsigned int mem_limit = 0;
static unsigned int mem_malloced = 0;
unsigned int slabs_clsid(unsigned int size) {
int res = 1;
if(size==0)
return 0;
size--;
while(size >>= 1)
res++;
if (res < POWER_SMALLEST)
res = POWER_SMALLEST;
if (res > POWER_LARGEST)
res = 0;
return res;
}
void slabs_init(unsigned int limit) {
int i;
int size=1;
mem_limit = limit;
for(i=0; i<=POWER_LARGEST; i++, size*=2) {
slabclass[i].size = size;
slabclass[i].perslab = POWER_BLOCK / size;
slabclass[i].slots = 0;
slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;
slabclass[i].end_page_ptr = 0;
slabclass[i].end_page_free = 0;
slabclass[i].slab_list = 0;
slabclass[i].list_size = 0;
slabclass[i].killing = 0;
}
}
static int grow_slab_list (unsigned int id) {
slabclass_t *p = &slabclass[id];
if (p->slabs == p->list_size) {
unsigned int new_size = p->list_size ? 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;
}
int slabs_newslab(unsigned int id) {
slabclass_t *p = &slabclass[id];
int num = p->perslab;
int len = POWER_BLOCK;
char *ptr;
if (mem_limit && mem_malloced + len > mem_limit)
return 0;
if (! grow_slab_list(id)) return 0;
ptr = malloc(len);
if (ptr == 0) return 0;
memset(ptr, 0, len);
p->end_page_ptr = ptr;
p->end_page_free = num;
p->slab_list[p->slabs++] = ptr;
mem_malloced += len;
return 1;
}
void *slabs_alloc(unsigned int size) {
slabclass_t *p;
unsigned char id = slabs_clsid(size);
if (id < POWER_SMALLEST || id > POWER_LARGEST)
return 0;
p = &slabclass[id];
#ifdef USE_SYSTEM_MALLOC
if (mem_limit && mem_malloced + size > mem_limit)
return 0;
mem_malloced += size;
return malloc(size);
#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 */
if (! (p->end_page_ptr || p->sl_curr || slabs_newslab(id)))
return 0;
/* return off our freelist, if we have one */
if (p->sl_curr)
return p->slots[--p->sl_curr];
/* if we recently allocated a whole page, return from that */
if (p->end_page_ptr) {
void *ptr = p->end_page_ptr;
if (--p->end_page_free) {
p->end_page_ptr += p->size;
} else {
p->end_page_ptr = 0;
}
return ptr;
}
return 0; /* shouldn't ever get here */
}
void slabs_free(void *ptr, unsigned int size) {
unsigned char id = slabs_clsid(size);
slabclass_t *p;
if (id < POWER_SMALLEST || id > POWER_LARGEST)
return;
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 ? 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;
return;
}