Memcached源码阅读四 内存初始化

  • 2021-01-11
  • 浏览 (73)

Memcached源码阅读四 内存初始化

Memcached作为内存cache服务器,内存高效管理是其最重要的任务之一,Memcached使用SLAB管理其内存,SLAB内存管理直观的解释就是分配一块大的内存,之后按不同的块(48byte, 64byte, … 1M)等切分这些内存,存储业务数据时,按需选择合适的内存空间存储数据。

Memcached首次默认分配64M的内存,之后所有的数据都是在这64M空间进行存储,在Memcached启动之后,不会对这些内存执行释放操作,这些内存只有到Memcached进程退出之后会被系统回收,下面分析下Memcached的内存初始化过程。

//内存初始化,settings.maxbytes是Memcached初始启动参数指定的内存值大小,settings.factor是内存增长因子
slabs_init(settings.maxbytes, settings.factor, preallocate);

#define POWER_SMALLEST 1   //最小slab编号
#define POWER_LARGEST  200 //首次初始化200个slab

//实现内存池管理相关的静态全局变量

static size_t mem_limit = 0;//总的内存大小
static size_t mem_malloced = 0;//初始化内存的大小,这个貌似没什么用
static void *mem_base = NULL;//指向总的内存的首地址
static void *mem_current = NULL;//当前分配到的内存地址
static size_t mem_avail = 0;//当前可用的内存大小

static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];//定义slab结合,总共200个

void slabs_init(const size_t limit, const double factor, const bool prealloc) {

    int i = POWER_SMALLEST - 1;
    //size表示申请空间的大小,其值由配置的chunk_size和单个item的大小来指定
    unsigned int size = sizeof(item) + settings.chunk_size;

    mem_limit = limit;//mem_limit是全局变量

    if (prealloc) {
           //支持预分配
        mem_base = malloc(mem_limit);//申请地址,mem_base指向申请的地址

        if (mem_base != NULL) {
              //mem_current指向当前地址
            mem_current = mem_base;
            //可用内存大小为mem_limit
               mem_avail = mem_limit;
        } else {
            //支持预分配失败
            fprintf(stderr, "Warning: Failed to allocate requested memory in"
                    " one large chunk.\nWill allocate in smaller chunks\n");
        }
    }

    //置空slabclass数组
    memset(slabclass, 0, sizeof(slabclass));
    //开始分配,i<200 && 单个chunk的size<单个item最大大小/内存增长因子

    while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {

        //size执行8byte对齐
        if (size % CHUNK_ALIGN_BYTES)
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);

        slabclass[i].size = size;
        //slab对应chunk的大小
        slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
       //slab对应的chunk的个数
        size *= factor;
       //size下一个值为按增长因子的倍数增长

        if (settings.verbose > 1) {
            //如果有打开调试信息,则输出调试信息
            fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
                    i, slabclass[i].size, slabclass[i].perslab);
        }
    }

    //循环结束时,size已经增长到1M
    power_largest = i;//再增加一个slab
    slabclass[power_largest].size = settings.item_size_max;
    //slab的size为item_size_max
    slabclass[power_largest].perslab = 1;//chunk个数为1

    //打印调试信息
    if (settings.verbose > 1) {
        fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
                i, slabclass[i].size, slabclass[i].perslab);
    }

    //读取环境变量T_MEMD_INITIAL_MALLOC的值    
    {
        char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
        if (t_initial_malloc) {
            mem_malloced = (size_t)atol(t_initial_malloc);
        }

    }

    if (prealloc) {
        //分配每个slab的内存空间,传入最大已经初始化的最大slab编号
        slabs_preallocate(power_largest);
    }
}

//分配每个slab的内存空间
static void slabs_preallocate (const unsigned int maxslabs) {
    int i;
    unsigned int prealloc = 0;

    for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
        if (++prealloc > maxslabs)
            return;
        
        //执行分配操作,对第i个slabclass执行分配操作
          if (do_slabs_newslab(i) == 0) {
            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);
        }
    }
}

//执行分配操作
static int do_slabs_newslab(const unsigned int id) {

    slabclass_t *p = &slabclass[id];//p指向第i个slabclass
    int len = settings.slab_reassign ? settings.item_size_max:p->size*p->perslab;
    char *ptr;
    
   
       //grow_slab_list初始化slabclass的slab_list,而slab_list中的指针指向每个slab
    //memory_allocate从内存池申请1M的空间
    if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
        (grow_slab_list(id) == 0) ||
          ((ptr = memory_allocate((size_t)len)) == 0)) {
        MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
        return 0;
    }

    memset(ptr, 0, (size_t)len);
    //将申请的1M空间按slabclass的size进行切分
    split_slab_page_into_freelist(ptr, id);

    p->slab_list[p->slabs++] = ptr;//循环分配
    mem_malloced += len;//增加已经分配出去的内存数
    MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);

    return 1;
}

//初始化slabclass的slab_class,而slab_list中的指针指向每个slab,id为slabclass的序号
static int grow_slab_list (const unsigned int id) {

    slabclass_t *p = &slabclass[id];
       //p指向第id个slabclass;
    
       if (p->slabs == p->list_size) {
        size_t new_size =  (p->list_size != 0) ? p->list_size * 2 : 16;//new_size如果是首次分配,则取16,否则按旧值的2倍扩容
        void *new_list = realloc(p->slab_list, new_size * sizeof(void *));//申请空间,这个空间是从系统分配,不是从内存池分配
        if (new_list == 0) return 0;
        p->list_size = new_size;//修改第id个slabclass的值
        p->slab_list = new_list;
    }
    return 1;
} 

//从内存池分配size个空间
static void *memory_allocate(size_t size) {
    void *ret;

    if (mem_base == NULL) {//如果内存池没创建,则从系统分配
        ret = malloc(size);
    } else {
        ret = mem_current;
        //size大于剩余的空间
        if (size > mem_avail) {
            return NULL;
        }

        //按8字节对齐
        if (size % CHUNK_ALIGN_BYTES) {
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
        }
        //扣除size个空间
        mem_current = ((char*)mem_current) + size;
        if (size < mem_avail) {
            mem_avail -= size;//更新剩余空间大小
        } else {
            mem_avail = 0;
        }
    }

    return ret;
}

//将ptr指向的内存空间按第id个slabclass的size进行切分
static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {

    slabclass_t *p = &slabclass[id];
    int x;
    //每个slabclass有多个slab,对每个slab按slabclass对应的size进行切分

    for (x = 0; x < p->perslab; x++) {
        do_slabs_free(ptr, 0, id);//创建空闲item
        ptr += p->size;
    }
} 

//创建空闲item
static void do_slabs_free(void *ptr, const size_t size, unsigned int id)
 {
    slabclass_t *p;
    item *it;

    assert(((item *)ptr)->slabs_clsid == 0);
    assert(id >= POWER_SMALLEST && id <= power_largest);//判断id有效性
    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->prev = 0;
    it->next = p->slots;//挂载到slabclass的空闲链表中
    if (it->next) it->next->prev = it;
    p->slots = it;

    p->sl_curr++;//空闲item个数+1
    p->requested -= size;//已经申请到的空间数量更新
    return;
}