C 工具库6:通用内存分配器
本篇将介绍一个通用内存分配器的实现.
上一篇的开头已经介绍过,这个实现是组合first fit pool和fix_obj_pool以处理不同大小的分配请求.
首先看由fix_obj_pool返回的内存的头结构:
struct head_fix
{
unsigned short idx;/*0-255,表示在fix_objs的下标*/
unsigned short chunk_idx;
};
这个头不会返回给上层应用,是给释放内存使用的.因为内存的请求是对齐到4字节的,小于1024字节的请求,被分成256个分配大小,分别是4,
8,12,....1024.每个大小的请求由一个fix_obj_pool处理.idx的作用就是记录是由哪个fix_obj_pool分配的内存.还有一点要注意的是,head_fix
本身占用了4个字节,所以fix_obj_pool分配的对象大小实际是请求大小对齐到4倍数的大小再加上4.例如如果分配1个字节,对齐到4就是4字节,
所以实际分配内存的大小是8,只有后面4个字节会返回给上层应用.
这种空间的浪费对于小的内存请求有点过分了,所以在实际使用中,这个所谓的通用内存分配器,其实不是为编译时就已经可以确定大小的对象
使用的,对于编译期就确定大小的对象,应该直接使用fix_obj_pool.只有那些在运行时才确定大小的内存分配请求,例如字符串,变长结构等
才应该使用通用内存分配器.
下面介绍chunk_idx的作用.
首先回顾fix_obj_pool的实现,一个chunk中的所有对象在地址上都是连续的,当chunk的空间不够时,会再分配一个chunk,然后将这些chunk按期首地
址大小的升序排序.这样,释放一个对象时,可以根据对象地址使用2分查找快速的找到对象归属的chunk.
虽然2分查找效率是比较高的,但最好还是能避免.在单独使用fix_obj_pool的情况小,一般可以大致估算到系统中最大可能会分配多少个这类对象.
在创建fix_obj_pool时,传入这个估算值,就可以生成一个大小基本符合要求的chunk,这样几乎在大多数情况下都避免了释放内存时的一次2分查找.
但对于通用内存分配器,我们不太好估计这个值,所以,chunk的初始大小一般都不会设得太大.为了避免二分查找,在分配分时,通过调用:
void *pool_alloc2(struct fix_obj_pool *pool,unsigned short *chunk_idx)
把chunk的下标一并返回,释放时直接把这个下标传进去就可以定位到正确的chunk了.
下面是通用内存分配器的代码:
mem_allocator.h
#ifndef _MEM_ALLOCATOR_H
#define _MEM_ALLOCATOR_H
struct allocator;
/*
* max:可以分配的最大请求字节数
*/
struct allocator *allocator_create(unsigned int max);
void allocator_destroy(struct allocator **);
void *allocator_alloc(struct allocator*,int);
void allocator_dealloc(struct allocator*,void*);
#endif
mem_allocator.c
#include "mem_allocator.h"
#include "../first_fit/first_fit.h"
#include "../first_fit/first_fit_define.h"
#include "../fix_obj_pool/fix_obj_pool.h"
#include "http://www.cnblogs.com/vector/vector.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define FIRST_FIT_DEFAULT_SIZE 1024*1024*16 //默认first_fit分配器大小是16MB
/*
* fix_obj_pool用于分配从1-1024字节大小的请求
* 请求对齐到4字节.所以总共有1024/4 = 256种大小
*/
struct allocator
{
unsigned int max_request; //可以处理的最大分配请求
struct fix_obj_pool *fix_objs[1024/4];
struct vector *first_fits;//存放first_fit
int last_dealloc_idx;
};
struct head_fix
{
unsigned short idx;/*0-255,表示在fix_objs的下标*/
unsigned short chunk_idx;
};
struct head_first_fit
{
unsigned int idx;//在first_fits中的下标
};
struct chunk;
extern void *pool_alloc2(struct fix_obj_pool*,unsigned short *chunk_idx);
extern void pool_dealloc2(struct fix_obj_pool *pool,unsigned short chunk_idx,void* obj);
struct allocator *allocator_create(unsigned int max)
{
struct allocator *al = malloc(sizeof(*al));
if(!al)
return 0;
memset(al,sizeof(*al),0);
al->max_request = max;
al->last_dealloc_idx = -1;
al->first_fits = 0;
int i = 0;
for(; i < 1024/4; ++i)
{
unsigned int real_size = (i+1)*4 + sizeof(struct head_fix);
al->fix_objs[i] = create_pool(real_size,1024,1);
if(!al->fix_objs[i])
{
allocator_destroy(&al);
return 0;
}
}
al->first_fits = vector_create(sizeof(struct first_fit_pool*),0);
struct first_fit_pool *first_fit = first_fit_create(FIRST_FIT_DEFAULT_SIZE);
if(!first_fit)
{
allocator_destroy(&al);
return 0;
}
VECTOR_PUSH_BACK(struct first_fit_pool*,al->first_fits,first_fit);
return al;
}
void allocator_destroy(struct allocator **al)
{
assert(al);
assert(*al);
unsigned int i = 0;
if((*al)->first_fits)
{
unsigned int size = vector_size((*al)->first_fits);
struct first_fit_pool **first_fits = VECTOR_TO_ARRAY(struct first_fit_pool *,(*al)->first_fits);
for( ; i < size; ++i)
first_fit_destroy(&first_fits[i]);
}
i = 0;
for( ; i < 1024/4;++i)
{
if((*al)->fix_objs[i])
destroy_pool(&(*al)->fix_objs[i]);
}
free(*al);
*al = 0;
}
void *allocator_alloc(struct allocator *al,int size)
{
assert(al);
unsigned int alloc_size = size;
if(size <= 0)
alloc_size = 1;
//将请求大小调整为4的倍数
alloc_size = alignsize(alloc_size);
if(alloc_size > al->max_request)
return 0;//无法处理这个请求
if(alloc_size > 1024)
{
struct head_first_fit *hff = 0;
//交给first fit分配器
alloc_size += sizeof(struct head_first_fit);
if(al->last_dealloc_idx >= 0)
{
//首先尝试从上次释放的first_fit分配
struct first_fit_pool *ff = VECTOR_GET(struct first_fit_pool *,al->first_fits,al->last_dealloc_idx);
assert(ff);
hff = (struct head_first_fit*)first_fit_alloc(ff,alloc_size);
}
if(!hff)
{
//遍历first_fits
struct first_fit_pool **ffs = VECTOR_TO_ARRAY(struct first_fit_pool *,al->first_fits);
unsigned int i = 0;
unsigned int end = vector_size(al->first_fits);
for( ; i < end; ++i)
{
hff = first_fit_alloc(ffs[i],alloc_size);
if(hff)
break;
}
if(!hff)
{
//分配一个新的first_fit;
struct first_fit_pool *ff = first_fit_create(FIRST_FIT_DEFAULT_SIZE);
if(!ff)
return 0;
VECTOR_PUSH_BACK(struct first_fit_pool*,al->first_fits,ff);
hff = first_fit_alloc(ff,alloc_size);
}
void *ret = (void*)((char*)hff + sizeof(*hff));
return ret;
}
}
else
{
//由fix_obj_pool处理
unsigned char idx = alloc_size/4 - 1;
unsigned short chunk_idx;
struct head_fix *hf = pool_alloc2(al->fix_objs[idx],&chunk_idx);
if(!hf)
return 0;
hf->idx = idx;
hf->chunk_idx = chunk_idx;
void *ret = (void*)((char*)hf + sizeof(*hf));
return ret;
}
}
void allocator_dealloc(struct allocator *al,void *ptr)
{
assert(al);
assert(ptr);
struct first_fit_chunk dummy;
//首先确定是哪种类型的分配器分配的
struct first_fit_chunk *ffc = (struct first_fit_chunk *)((int*)ptr - sizeof(struct head_first_fit)/4 - sizeof(dummy.tag)/4 - sizeof(dummy.size)/4);
if(ffc->tag == USED_TAG)
{
//由first_fit分配的
struct head_first_fit *hff = (struct head_first_fit *)((int*)ptr - sizeof(struct head_first_fit)/4);
if(hff->idx < vector_size(al->first_fits))
{
struct first_fit_pool *ff = VECTOR_GET(struct first_fit_pool *,al->first_fits,hff->idx);
assert(ff);
first_fit_dealloc(ff,hff);
al->last_dealloc_idx = hff->idx;
}
}
else
{
struct head_fix *hf = (struct head_fix *)((int*)ptr - sizeof(struct head_fix)/4);
if(hf->idx <= 255)
{
pool_dealloc2(al->fix_objs[hf->idx],hf->chunk_idx,ptr);
}
}
}