SGI第二级空间配置器较第一级空间配置器加入了内存池(memory pool)管理,即次层配置。当所申请的空间大于128bytes时,直接调用一级空间配置器处理,小于128bytes时,使用次层配置器管理。申请的空间不足8的倍数,默认提升为最近的8的整数倍的空间大小。
__default_alloc_template
模板类的声明以及一些成员的初始化
//以下是第二级配置器
//notice:没有"template"类型参数,第二个参数没有派上用处
//第一个参数用于多线程环境下,目前不讨论
template <bool threads, int inst>
class __default_alloc_template
{
private:
//ROUND_UP()将bytes提升至8的倍数
static size_t ROUND_UP(size_t bytes)
{
return ((bytes + __ALIGN - 1) & ~(__ALIGN - 1));
}
private:
union obj //free_lists节点构造
{
union obj * free_list_link;
char client_data[1];
};
private:
//16个free_lists
static obj * volatile free_list[__NFREELISTS];
//根据区块大小,决定使用第n号free-list, n从1算
static size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes) + __ALIGN - 1) / __ALIGN - 1);
}
//返回一个大小为n的对象,并可能加入大小为n的其他区块到free list
static void *refill(size_t n);
//配置一大块空间,可容纳nobjs个大小为"size"的区块
//如果配置nobjs个区块有所不便,nobjs可能会降低
static char* chunk_alloc(size_t size, int& nobjs);
// chunk allocation state
static char *start_free; //内存池起始位置
static char *end_free; //内存池结束位置
static size_t heap_size;
public:
static void * allocate(size_t n);
static void deallocate(void *p, size_t n);
static void * reallocate(void *p, size_t old_sz, size_t new_sz);
};
template <bool threads, int inst>
char *__default_alloc_template::start_free = 0;
template <bool threads, int inst>
char *__default_alloc_template::end_free = 0;
template <bool threads, int inst>
size_t __default_alloc_template::heap_size = 0;
template <bool threads, int inst>
typename __default_alloc_template::obj * volatile
__default_alloc_template::free_list[__NFREELISTS] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
if(n > (size_t)__MAX_BYTES)
{
return malloc_alloc::allocate(n);
}
每次配置一大块内存,维护其对应的自由链表free_lists中16个free_list,每个free_list的union obj串的每一个节点都是8的倍数的内存,链表的结构如
union obj
{
union obj * free_list_link;
char client_data[1];
}
//一物两用,保存下一个free_list的指针,也保存实际区块的内容
free_lists的结构如图:
当所allocate的内存小于128bytes,调用allocate
对于其实现,这里分为以下几种情况
obj * volatile *my_free_list;
obj * result;
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
//调整free list
*my_free_list = result->free_list_link;
return (result);
if(result == 0)
{
//没找到free_list,准备重新填充free list
void *r = refill(ROUND_UP(n));
//refill为free_list重新填充空间,新的空间取自内存池(由chunk_alloc()完成)
return r;
}
以下是完整代码阐述
template<bool threads, int inst>
void * __default_alloc_template::allocate(size_t n)
{
obj * volatile *my_free_list;
obj * result;
//大于128字节就调用第一级配置器
if(n > (size_t)__MAX_BYTES)
{
return malloc_alloc::allocate(n);
}
//寻找16个free lists中适当的一个
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if(result == 0)
{
//没找到free_list,准备重新填充free list
void *r = refill(ROUND_UP(n));
return r;
}
//调整free list
*my_free_list = result->free_list_link;
return (result);
}
allocate获取可用区块,如果没有可用的free_lists,就要调用refill()填充空间,新的空间取自内存池(由chunk_alloc()完成),nobjs为20,以引用形式传入chunk_alloc即缺省取得20个新节点(新区块),当内存池空间不足,最终nobjs可能小于20.
refill函数做了两件事,一是利用chunk_alloc申请空间,二是将申请的空间第一个块传给客端,其他19(甚至更少)个块串起来
refill()实现是这样的:
int nobjs = 20;
//调用chunk_alloc(),尝试取得nobjs个区块作为free_list的新节点
//注意参数nobjs是pass by reference
char * chunk = chunk_alloc(n, nobjs);
obj * volatile * my_free_list;
obj * result;
obj * current_obj, * next_obj;
int i;
//如果只获得一个区块,这个区块就分配给调用者,free_list无新节点
if(1 == nobjs)
return (chunk);
//my_free_list指向自己在free_lists中的位置
my_free_list = free_list + FREELIST_INDEX(n);
//以下在chunk空间内建立free list
result = (obj *)chunk; //这一块准备返回给客端
//以下导引free_list指向新配置的空间(取自内存池)
*my_free_list = next_obj = (obj *)(chunk + n);
//以下将free_list的各节点串接起来
for(i = 1; ; i++)
{
//从1开始,因为第0个将返回给客端
current_obj = next_obj;
next_obj = (obj*)((char *)next_obj + n);
if(nobjs - 1 == i)
{
current_obj->free_list_link = 0;
break;
}
else
{
current_obj->free_list_link = next_obj;
}
}
return (result);
end_free-start_free
,来判断内存池剩余的水量 char * result;
size_t total_bytes = size * nobjs;
size_t bytes_left = end_free - start_free; //内存池剩余空间
分为以下几种情况
if(bytes_left >= total_bytes)
{
//内存池剩余空间完全满足需求量
result = start_free;
start_free += total_bytes;
return (result);
}
else if(bytes_left >= size)
{
//内存池剩余空间不能完全满足需求量,但足够供应一个含以上区块
nobjs = bytes_left / size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return (result);
}
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
//以下试着让内存池的残馀零头还有利用价值
if(bytes_left > 0)
{
//内存池还有一些零头,先配置给适当的free_list
//首先寻找适当的free_list
obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);
//调整free_list,将内存池中的残馀空间编入
((obj*)start_free)->free_list_link = *my_free_list;
*my_free_list = (obj*)start_free;
}
//配置heap空间,用来补充内存池
start_free = (char*)malloc(bytes_to_get);
int i;
obj *volatile * my_free_list, *p;
//搜索适当的fee_list
//适当即"尚有未用区块。且区块足够大"
for(i = size; i <= __MAX_BYTES; i += __ALIGN)
{
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if(0 != p)
{
//free_list 内尚有未用区块
//调整free_list以释放未用区块
*my_free_list = p->free_list_link;
start_free = (char*)p;
end_free = start_free + i;
return (chunk_alloc(size, nobjs));
}
}
end_free = 0;
start_free = (char*)malloc_alloc::allocate(bytes_to_get);
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return (chunk_alloc(size, nobjs));
如图:内存池实际操作
以下是chunk_alloc的完整代码
//假设size已经适当上调至8的倍数
//注意参数nobjs是pass bu reference
template <bool threads, int inst>
char* __default_alloc_template::chunk_alloc(size_t size, int& nobjs)
{
char * result;
size_t total_bytes = size * nobjs;
size_t bytes_left = end_free - start_free; //内存池剩余空间
if(bytes_left >= total_bytes)
{
//内存池剩余空间完全满足需求量
result = start_free;
start_free += total_bytes;
return (result);
}
else if(bytes_left >= size)
{
//内存池剩余空间不能完全满足需求量,但足够供应一个含以上区块
nobjs = bytes_left / size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return (result);
}
else
{
//内存池剩余空间连一个区块额大小都无法提供
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
//以下试着让内存池的残馀零头还有利用价值
if(bytes_left > 0)
{
//内存池还有一些零头,先配置给适当的free_list
//首先寻找适当的free_list
obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);
//调整free_list,将内存池中的残馀空间编入
((obj*)start_free)->free_list_link = *my_free_list;
*my_free_list = (obj*)start_free;
}
//配置heap空间,用来补充内存池
start_free = (char*)malloc(bytes_to_get);
if(0 == start_free)
{
//heap空间不足,malloc()失败
int i;
obj *volatile * my_free_list, *p;
//搜索适当的fee_list
//适当即"尚有未用区块。且区块足够大"
for(i = size; i <= __MAX_BYTES; i += __ALIGN)
{
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if(0 != p)
{
//free_list 内尚有未用区块
//调整free_list以释放未用区块
*my_free_list = p->free_list_link;
start_free = (char*)p;
end_free = start_free + i;
return (chunk_alloc(size, nobjs));
}
}
end_free = 0;
start_free = (char*)malloc_alloc::allocate(bytes_to_get);
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return (chunk_alloc(size, nobjs));
}
}