Chinaunix首页 | 论坛 | 博客
  • 博客访问: 644234
  • 博文数量: 227
  • 博客积分: 8017
  • 博客等级: 中将
  • 技术积分: 2069
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-08 22:50
文章分类

全部博文(227)

文章存档

2011年(10)

2010年(55)

2009年(28)

2008年(134)

我的朋友

分类: 服务器与存储

2010-09-18 21:07:47

说明: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) |
给主人留下些什么吧!~~

chinaunix网友2011-06-22 14:47:40

MEMCACHED_SLABS_ALLOCATE这样的方法到底在那个文件?能介绍下吗,居然没有搜索到...还有MEMCACHED_SLABS_FREE等等一系列大写名字的函数找不到

chinaunix网友2011-06-22 14:47:40

MEMCACHED_SLABS_ALLOCATE这样的方法到底在那个文件?能介绍下吗,居然没有搜索到...还有MEMCACHED_SLABS_FREE等等一系列大写名字的函数找不到