问题的引入
实现malloc-free,用来管理64k的内存。这是一道面试题,也是一道很好的设计题
解决思路
1.malloc(size_t size), free(void *p)是用户api, 释放的时候需要知道释放空间的大小,并且需要放入到一个空闲链表里面去,可以这样设计
typedef struct item{
uint16_t size;
union {
char mem[4];
struct item *next;
}mn;
}__attribute__ ((packed)) item;
2.采用union,如果分配给用户,则为可以使用内存,加入到空闲链表,则为链表指针。这里的指针大小为4个字节,因此分配给用户的最小长度为4字节,如果用户申请的长度小于4个字节,应该分配4个字节。
如果为16位机器,则应该将联合体改为union {char mem[2];struct item *next;}mn;
3.为了避免内存碎片,这里需要将释放的两个相邻的区域拼接成一个较大的空间,设计一个splice函数。
4.申请,首先找到第一个比申请内存大的空间,然后划分一部分分配给用户,划分函数为cut.
实现
define ITEM_SIZE (sizeof(item) - sizeof(void *))
typedef struct item{
uint16_t size;
union {
char mem[4];
struct item *next;
}mn;
}__attribute__ ((packed)) item;
static item *freelist=NULL;
void init(void *memory, uint16_t size)
{
assert(memory != NULL && size > sizeof(item));
freelist = (item *) memory;
freelist->mn.next = NULL;
freelist->size = size - ITEM_SIZE;
}
static bool cut(item *it, uint16_t size, item **n)
{
assert(it != NULL && n != NULL);
if((uint16_t) (it->size - size) < (uint16_t) sizeof(item))
return false;
*n = (item *)(it->mn.mem + size);
(*n)->size = it->size - size - ITEM_SIZE;
(*n)->mn.next = it->mn.next;
it->size = size;
return true;
}
注意item的定义采用的是联合union,使用非常方便
static void splice(item *left, item *right)
{
if(left == NULL || right == NULL)
return ;
char *start = left->mn.mem + left->size;
if(start != (char *)right)
return ;
left->size = left->size + right->size + ITEM_SIZE;
left->mn.next = right->mn.next;
}
分配算法
首先查看分配的空间是否小于4,如果小于,则修改为4,便利空闲链表,找到地一个大于申请空间的块,如果可以cut,就cut一部分,不能cut,就分配当前块
void * malloc(uint16_t size)
{
if(size < 4)
size = 4;
item *prev = NULL, *cur = freelist;
item *n = NULL;
while(cur != NULL) {
if(cur->size > size) {
bool c = cut(cur, size, &n);
if(c == false)
n = cur->mn.next;
if(prev == NULL)
freelist = n;
else
prev->mn.next = n;
return cur->mn.mem;
}
prev = cur;
cur = cur->mn.next;
}
return NULL;
}
释放算法,首先将释放的地址左移两个字节,然后转换为item结构,遍历空闲链表,找到地一个item大于插入item的结构,插入item,并且splice
这里需要注意的是当空闲队列为空的情况,或者释放item的地址最大的情况
void free(void *p)
{
if(p == NULL)
return ;
item *cur = freelist, *prev = NULL;
item *pit = (item *)((char *)p - ITEM_SIZE);
while(cur != NULL) {
if(cur > pit) {
if(prev == NULL) {
pit->mn.next = freelist;
freelist = pit;
} else {
pit->mn.next = prev->mn.next;
prev->mn.next = pit;
}
splice(pit, cur);
splice(prev, pit);
return ;
}
prev = cur;
cur = cur->mn.next;
}
if(prev == NULL) {
pit->mn.next = freelist;
freelist = pit;
} else {
pit->mn.next = prev->mn.next;
prev->mn.next = pit;
}
splice(prev, pit);
}
测试---采用20k的内存测试
#define K_1 1024
#define K_64 (K_1 * 20)
char memory[K_64];
int
main ( int argc, char *argv[] )
{
init(memory, K_64);
const int count = 100000;
void *p[count];
int len = 0;
for(int i = 1; i < count; i++) {
p[len++] = malloc(i);
}
for(int i = 0; i < len; i++) {
free(p[i]);
}
len = 0;
for(int i = 1; i < count; i++) {
p[len++] = malloc(i % 20 );
}
assert(freelist == NULL);
for(int i = 0; i < len; i = i + 2) {
free(p[i]);
}
for(int i = 1; i < len; i = i + 2) {
free(p[i]);
}
assert(freelist->size == K_64 - ITEM_SIZE);
assert(freelist->mn.next == NULL);
printf("sizeof(item):%d\n", sizeof(item));
return 0;
} /* ---------- end of function main ---------- */
演化
有一种观点认为:需要设计自己的内存分配器,比如stl中的内存分配,memcached中的slab分配,leveldb中memtable中内存分配
一种观点认为操作系统中内存管理已经设计的够好了,不需要设计自己的内存分配器
————————————————
版权声明:本文为CSDN博主「yuan_da_xing」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yuan_da_xing/article/details/8395704
来源:oschina
链接:https://my.oschina.net/u/4000302/blog/3222923