STL学习_SGI二级空间配置器源码剖析

//二级空间配置器 __defaule_alloc_template

如果感觉光看代码难以理解,可以看看上篇博客介绍了二级空间配置器是怎样进行内存分配的。

enum{__ALIGN = 8};//最小申请的空间的大小
enum{__MAX_BYTES = 128};//能最大申请的空间的大小
//SGI第二配置器有16个free_lists
enum{__NFREELISTS = __MAX_BYTES / __ALIGN};//free_lists的个数

template<bool threads, int inst>
class __default_alloc_template
{
private:
    //将客户申请的空间的大小上调至8的倍数
    static size_t ROUND_UP(size_t bytes)
    {
        return (((bytes) + __ALIGN-1)&~(__ALIGN - 1));
    }  
private:
    //16个free_lists的节点结构
    //union能够实现一物两用的效果,1.obj可被视为一个指针,指向相同形式的另一    //个obj。2.obj可被视为一个指针,指向实际区块。
    union obj{
        union obj * free_list_link;//指向下一个节点的指针
        char client_data[1];//记录此时节点数据
    };
private:
    static obj * volatile free_list[__NFREELISTS];//16个free_lists
    //计算客户申请的空间的大小在哪个free_lists自由链表
    static size_t FREELIST_INDEX(size_t bytes)
    {
        return (((bytes) + __ALIGN-1)/ __ALIGN - 1);
    }
    static char *start_free;//内存池起始位置
    static char *end_free;//内存池结束位置
    static size_t heap_size;//附加量
private:
    static void *refill(size_t n);
    static char *chunk_alloc(size_t size, int &nobjs);
public:
    //空间配置函数
    static void *allocate(size_t n)
    {
        obj * volatile * my_free_list;
        obj * result;
        //客户申请的空间大于128采用一级空间配置器进行空间的申请
        if(n > (size_t __MAX_BYTES){
            return (malloc_alloc::allocate(n));
        }
        //my_free_list这个二级指针指向客户申请空间大小适合的自由链表free_lists
        my_free_list = free_list + FREELIST_INDEX(n);
        //*my_free_list这个指针指向的是相对应的还没有给客户端分配的自由链表的起始
        //位置
        result = *my_free_list;
        //没有找到可用的free_list,准备查看有没有可用的内存池来填充自由链表
        if(0 == result){
            void *r = refill(ROUND_UP(n));
            return r;
        }
        //找到可用的自由链表,调整自由链表,让*my_free_list这个指针指向还没有给客        //户分配的自由链表的起始位置,也就是result的下一个节点的位置
        *my_free_list = result->free_list_link;
         return result;
    }
    //空间释放函数
    static void deallocate(void *p, size_t n)
    {
        obj *q = (obj *)p;
        obj * volatile * my_free_list;
        if(n > (size_t)__MAX_BYTES){
            malloc_allloc::deallocate(p, n);
            return;
        }
        //释放已经给客户分配出去的p空间,也就是让p空间重新连接到自由链表上
        //也就是先让p空间的指针指向下个节点也就是自由链表的起始位置,然后让自由链        //表的起始位置重新指向p空间
        my_free_list = free_list + FREELISTS_INDEX(n);
        q->free_list_link = *my_free_list;
        *my_free_list = q;
    }
};

//对__default_alloc_template这个类内的数据成员的定义与初值设定
template<bool threads, int inst>
char *__default_alloc_template<tnreads, inst>::start_free = 0;

template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::end_free = 0;

template<bool threads, int inst>
size_t __default_alloc_template<threads, inst>::head_size = 0;

//这里的数据成员free_list,由于free_list的类型obj * volatile是模板类型声明的
//并且这个函数名也是模板类型内声明的,所以这里对free_list的定义与处值的设定
//用两个模板类__default_alloc_template<threads, inst>
template<bool threads, int inst>
__default_alloc_template<thread, inst>::obj * volatile
__default_alloc_template<threads, inst>::free_list[__NFREELISTA]=
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,};

//重新填充函数,当发现自由链表没有可用区块了,就调用这个函数从内存池中找到空间
//重新填充自由链表
template<bool threads, int inst>
void *__default_alloc_template<threads, inst>::refill(size_t n)
{
    int nobjs = 20;
    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_list + NFREELIST_INDEX[n];
    result = (obj *)chunk;//这一块准备返回给客户
    //chunk指向的是新free_list的起始位置,这里让*my_free_list指向新配置的
    //空间,取自内存池
    *my_free_list = next_obj = (obj *)(chunk + n);
    //将free_list的各个节点用指针串起来,从第一个开始,因为第零个区块
    //要返回给客户
    for(i = 1; ; ++i){
        current_obj = next_obj;
        next_obj = (obj *)((char *)next_obj + n);
        if(nobjs - 1 == i){
            //最后一个节点的指针指向NULL
            current_obj->free_list_link = 0;
            break;
        }else{
            current_obj->free_list_link = next_obj;
        }
    }
    return (result);
}

//从内存池中取出空间给free_list使用
template<bool threads, int inst>
char *__defaule_alloc_template<threads, inst>::chunk_alloc(size_t size, int &nobjs)
{
    char *result;
    size_t total_bytes = size * nobjs;//需要给free_list分配的空间大小
    size_t bytes_left = end_free - start_free;//内存池剩余空间大小
    //内存池的空间足够满足需求量
    if(bytes_left >= total_bytes){
        result = start_free;//result得到新的free_list的起始位置
        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);
        //内存池还有一些零头,先分配给适当的free_list
        if(bytes_left > 0){
            obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);
        ((obj *)start_free)->free_list_link = *my_free_list;
        *my_free_list = (obj *)start_free;
        }
        //内存池一点零头都没有了,就配置heap空间
        start_free = (char *)malloc(bytes_to_get);
        //如果系统的heap空间不足,malloc()失败
        if(0 == start_free){
            int i;
            obj * volatile * my_free_list, *p;
            //遍历搜索看还有没有用的free_list吗,
            for(i = size; i < __MAX_BYTES; i += __ALIGN){
                my_free_list = free_list + FREELIST_INDEX[i];
                p = *my_free_list;
                //free_list有没有用的区块
                if(0 != p){
                    
                    *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;
        //递归调用自己为了修正nobjs
        return chunk_alloc(size,nobjs);
    }

}

你可能感兴趣的:(STL学习_SGI二级空间配置器源码剖析)