内存slab分配器的设计与实现

本文介绍了内存slab分配器的基本思想与实现细节,包括如何减少页内碎片、对象分配与回收机制,以及核心数据结构的设计等。

原始出处: http://blog.chinaunix.net/uid-20687780-id-3154025.html


按: 从slab管理来看,当需要释放对象时,如果其中有page没有释放的,会导致页泄漏。



内存slab分配器最初思想来自Solaris的内核态小数据结构(一页以内)的内存分配,受到Solaris的影响,Linux内核也采用类似思想来减少页内碎片,其基本思想是:一次向内核获取整数页,slab根据数据结构的大小进行划分为一个个小的数据结构,当需要时直接从该链表上摘取一个返回应用程序,当应用程序释放时,而非真正释放,只需要该空间放回到链表中,当分散的一页多块又聚集一页时,又会拼成一页,同时判断slab空闲的页数,如果空闲页超过一定的页数,就会向系统释放一定的页数。一个slab分配器只能管理一个指定大小的数据结构分配。总体结构如下:

一个slab分配器管理许多页,而每一页又管理许多的对象。为了有效管理页,即便于分配与释放,又根据页内对象的数量将页分为3个状态:

1.空闲(free)状态:该页没有分配出去任何对象

2.部分分配(partial)状态:该页只有部分对象被分配

3.完全分配(full)状态:该页所有对象都被分配出去。

除此之外,为了方便管理页内对象通常需要将对象进行对齐。这样一页内部的结构如下:

对象空间

对齐

对象空间

对齐

….......

空闲空间

页描述性信息

而提供分配的API主要为:

1.创建一个slab分配器

umem_cache_create(const char *name,size_t size,unsignd int align,

unsigned int flags,void (*ctor)(void *),void (*dctor)(void *))

2.分配一个对象

void *umem_cache_alloc(umem_cache_t *cp);

3.释放一个对象

void *umem_cache_free(umem_cache_t *cp,void *buf)

4.销毁一个slab分配器

void umem_cache_destroy(umem_cache_t *cp)

主要的数据结构则为页管理对象slab管理对象

为了判断一中空闲对象的数量,采用引用计数的方式来实现,具体页管理对象为:

struct slab {

struct list list;

struct list slab_list;

int used;

}

而具体的slab管理对象则是链接三个状态的页和其它管理信息,具体如下:

typedef struct umem_cache {

const char *name;//slab名称

unsigned long size;//对象大小

unsigned int nrsize;//一页中对象的数量

….....

void (*ctor)(void *);//对象初始化函数

void (*dtor)(void *);//对象销毁函数

struct list slabs_partial;//部分分配链表

struct list slabs_full;//完全分配链表

struct list slabs_free;//完全空闲链表

union {//用于分配对象的同步机制,按照标志进行设置

sem_t sem;

pthread_spinlock_t spinlock;

}lock;

}umem_cache_t;

当创建一个slab分配器时,则根据标志进行创建,并想(按:向)内存获取一页数据用于预分配(这里将具体源代码放在一个函数中,源码分布在多个函数中)

umem_cache_t *umem_cache_create(const char *name,size_t size......)

{

…....

cachep = (umem_cache_t *)calloc(1,sizeof(umem_cache_t));

…........

//这里从进程地址空间的映射区,也可通过从堆分配一页空间

mem=mmap(NULL,cachep->pagesize,....);

//通过页的起始地址来获取页的尾地址

slab = (struct slab *)(((ptr_t)mem | (pagesize - 1)) - sizeof(struct slab));

…...

for(i = 0;i<nrsize;i++) {<="" p="" style="word-wrap: break-word;">

struct list *n;

n = (struct list *)(mem + i*cachep->size);

list_init(n);

list_add_head(&slab->slab_list,n);

}

cachep->nrpage++;

list_add_head(cachep->slabs_free,&slab->list);

cachep->nrfree += nrsize;

….......

}

分配一个对象,直接从slabs_parital中分配,如果该空间已满,就从slabs_free中获取一页再进行分配:

void *umem_cache_alloc(umem_cache_t *cachep) {


__lock_wait(cachep);

//如果free为空,再获取一页

if(!cachep->nrfree && (get_one_page(cachep,&cachep->slabs_free))<0)

goto __last;

//先从paritial中分配

if(!list_empty(&cachep->slabs_patial)) {

mem = __alloc_node(cachep,cachep->slabs_partial.next);

goto __last;

}

if(!list_empty(&cachep->slabs_free)) {

list_move(&cachep->slabs_partial,&cachep->slabs_free.next);

mem = __alloc_node(cachep,cachep->slabs_parital.next);

}

__last:

{

__lock_post(cachep);

if(mem) {

memset(mem,0,cachep->size);

if(cachep->ctor)//调用构造函数初始化

cachep->ctor(mem);

}

}

}

当释放一个对象时,需要将对象放入对应页链表中,并修改信息,同时根据对象的数量放到slab中不同的状态链表中.

void umem_cache_free(umem_cache_t *cachep,void *mem)

{

if(mem && cachep->dtor)

cachep->dtor(mem);

…..

list_init(mem);

list_add_head(slab_list,mem);

…......

check_free(cachep);

}

当销毁一个对象时,客户端需要保证所有的对象已经被释放,否则出现页没有被释放,造成页泄漏

void umem_cache_destroy(umem_cache_t *cp)

{

….....

//直接遍历free链表,释放页空间

list_for_each_entry_del(slab,n,&cachep->slabs_free,list)

{

release_one_page(cachep,slab);

}

…......

}

源码见:https://github.com/ytaoWang/utillib

参考资料

1.fuseslab源码阅读

2.Linux内核slab分配器

3.Jeff Bonwick,The Slab Allocator:An Object-Caching Kernel Memory Allocator,Sun Microsystem

4. OpenSolaris源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值