引言:应用级别的内存分配器的作用主要在于减少malloc函数的调用,降低系统的内存碎片。作为高性能的服务器,一般都会有自己的内存分配方案。slab作为一款Linux内核的经典内存分配方式,应用在很多的应用级别的软件上,比如说Memcached 等。
今天的主题就分享一下最近写的slab的一个简单的Demo,在于分享,代码有些粗糙,比如缺少对于内存字节的比例因子,缺少关于内存不足的情况下重分配等。不过各种应用各有各自的用途,在实现某些cached操作的软件中,由于带有LRU 算法等,在内存快使用完后就采取淘汰策略,所以不一定在内存不足的情况下重新给操作系统分配。
Slab内存分配器原理:
此图片来自自己的另一篇博文,如若不能显示,请google slab 内存分配器
slab内存分配器的原理大致如图所示,由index 确定一个由slot 组成的链表,这里也可以设置为一个固定长度大小的结构,然后划分为不同大小的slot
两种方式都有自己的好处,采用链表的方式可以动态增长,采用固定长度大小的块结构结构清晰,可以采用链式的数组构造slot,然后由empty_index 来确定空闲的slot,本篇博文就是讲解采用了这种方式的管理的。稍后会有详解,这里只是作为简单介绍。
我们只需要了解可以通过对齐后的内存大小找到index,然后通过index 找到slot数组,通过empty_index找到空闲的slot,改变empty_index 使其指向下一个空闲的slot。
对齐宏:
#define ALIGN(a,size) ((a+size-1)&(~(size-1)))
a为申请的内存大小,size为内存对齐大小
比如说 a = 127 size = 128 , 那么小于128 的将a转化为128,总的来说,就是128的整数倍。
很多人说这样的方式会导致内存浪费,但是至今几乎都是采用这种方式。在没有找到一个更好的方式以前,这个也许就是最好的方式吧。
通过对齐后的内存大小,找到对应的index 是很容易的。比如说,我们的内存对齐是64,我们只需要将申请的 a>>6 .
#define MC_SLOT_ALIGN_SIZE (64)
我们定义了默认的内存字节就是64Byte.可以通过自己的喜好做修改
解释一下这里的命名方式,CHUNK_ARRAY 就是由index 作为下标的数组。
我们定义
#define MC_CHUNK_ARRAY_SIZE (16)
就是说,index最大为15 0 不做为index。
每一个slot 的结构是这样定义的:
data 指针作为连接slot 数组的每一个槽的指针存在着,另一种方式是设置为 void *data ,这样可以复用 data指针,在申请出来后可以作为指向数据的指针。
mc_address *start 用于指向slot申请的真正的内存地址,一般由malloc来申请。
mc_address *end 指向内存的末尾。 end - start == slot_length
slot_index 用于表示此slot是位于slot数组的下标
mc_chunkt_t *which_chunk_p 是用来表示属于哪一个chunk 的,这个指针可以在释放的时候降低接口复杂度。
下面看看chunk的结构表示:
chunk_length 为宏定义的MC_CHUNK_ARRAY_SIZE
slots 是一个指针,指向不同的slot数组,这里的slot数组也是通过动态分配的。后面会贴出相应代码。
slots_size是这个chunk下的slot数组中slot的内存大小。即end-statr
empty_slot_index是空闲的slot的index,这中数据结构可以查看类似的nginx的connection数组。
last_slot_num作为标示剩余的slot数量
slots_array_num是slot的数组的大小
下面看看头文件中的函数声明:
大致就是这几个函数,功能有说明。
总结:slab内存分配器的核心在于理解通过对齐后的内存大小得到index