memcache中管理内存的数据结构如下:
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]用于保存slab,预分配内存池时调用的是void slabs_init(const size_t limit, const double factor) 函数,其中limit是内存池的最大容量,factor是分配时的增长因子.
比方说,加入factor是2,第一个在slabclass数组中的slab的每个item大小是128字节,那么下一个slab每个item的大小就是128*2,再下一个就是128*2*2(注意,为了简化问题的说明,上面没有考虑地址对齐的因素).
在预分配内存池时,最多给每个slab保存item的容量是1M内存,这个数值由#define POWER_BLOCK 1048576决定.
因此,slab中的几个元素在预分配内存时是这么定的:
size有一个起始值,这个值以后的增长由factor决定,增长的过程前面已经阐述过了;
perslab保存的是一个slab存放的item数量,因此perslab = POWER_BLOCK / slabclass[i].size;
如果预先分配一段内存供使用的话,也就是没有定义DONT_PREALLOC_SLABS宏,那么就调用slabs_preallocate进行预分配内存.
其中,end_page_ptr指向这个预分配好的指针,end_page_free表示的是目前空闲可用item的数量,在预分配时,这个值与perslab相同.
在这个内存池模型中,每个page实际上是一个数组,数组中每个元素的大小就是这个slab中item的大小.
另外,slots保存的是释放出来的item指针,sl_total表示总的数量,sl_curr表示的是目前可用的已经释放出来的item数量.
每一次要分配内存的时候,首先根据需要分配的内存大小在slabclass数组中查找索引最小的一个大于所要求内存的slab,如果slots不为空,那么就从这里返回内存,否则去查找end_page_ptr,如果也没有,那么就只能返回NULL了.
每一次释放内存的时候,同样的找到应该返回内存的slab元素,改写前面提到的slot指针和sl_curr数.
有点仓促,以后再完善~~