內存分配器三

已經實現了自己的malloc函數Imalloc,但是內存管理還是需要calloc,free,relloc函數來管理內存:
calloc:在內存的動態存儲區中分配n個長度爲size的連續空間,函數返回一個指向分配起始地址的指針,並將內存空間置0,在32位機上是4bytes一個內存塊大小;如果分配不成功,返回NULL。

 void *Icalloc(size_t number,size_t size){
    size_t *new;
    size_t s4,i;
    new=Imalloc(number*size);
    if(new){
        s4=align4(number*size)<<2;
        for(i=0;i<s4;i++)
            new[i]=0;
    }
    return (new);
} 

在free的時候有兩個問題需要解決:驗證需要被free的塊,避免內存碎片。
驗證需要被free塊需要做兩件事:是否在之前malloc所分配的區域內;該地址確實是通過malloc分配的。問題一比較地址就可以了。問題二有兩個方法:在meta-data中添加magic number,free的時候檢查該值;添加一個magic pointer,指向數據區的第一個字節。採用方法二。在避免碎片的時候需要合併小的內存塊,需要將該內存塊的前後合併起來,但是單向鏈表難以操作,所以講meta-data改爲雙向鏈表。由於數據結構改變,相應的需要修改之前的函數。

typedef struct s_block *p_block;

struct s_block{
    size_t size;
    p_block next;
    p_block prev;
    int free;
    void *ptr;
    char data[1];
};

驗證地址合法性:

p_block get_block(void *p){
    char *temp;
    temp=p;
    return(p=temp-=META_DATA_SIZE);
}

int valid_addr(void *p){
    if(base){
        if(p>base&&p<sbrk(0))
            return (p==(get_block(p)->ptr));
    }
    return (0);
}

合併內存塊:

p_block fusion(p_block b){
    if(b->next&&b->next->free){
        b->size+=META_DATA_SIZE+b->next->size;
        b->next=b->next->next;  
        if(b->next)
            b->next->prev=b;
    }
    return (b);
}

free函數:
驗證指針通過:
1. 獲取內存塊的地址
2. 標記free爲1
3. 如果存在之前的塊並且爲free爲1,合併這兩塊內存
4. 同樣處理最後一塊
5. 如果是最後一塊內存塊
6. 如果沒有內存塊,返回最初狀態(base 置爲NULL)
如果驗證不通過就什麼也不處理。
int brk(const void *addr)將break設置到addr處

void Ifree(void *p){
    p_block b;
    if(valid_addr(p)){
        b=get_block(p);
        b->free=1;
        if(b->prev&&b->prev->free)
            b=fusion(b->prev);
        if(b->next)
            fusion(b);
        else{
            if(b->prev)
                b->prev->next=NULL;
            else
                base=NULL;
            brk(b);
        }
    }
}

realloc 函數:釋放老的動態內存塊,按照給出的尺寸分配新的動態內存塊,老的內存塊的內容儘量複製到新的內存塊

複製內存:

void copy_block(p_block src,p_block dst){
    int *sdata,*ddata;
    size_t i;
    sdata=src->ptr;
    ddata=dst->ptr;
    for(i=0;i*4<src->size&&i*4<dst->size;i++)
        ddata[i]=sdata[i];
}

重寫realloc:
1. 使用Imalloc分配新的內存塊
2. 從舊的內存塊複製內容到新的內存塊
3. 釋放老的內存塊
4. 返回新的指針
如果:
1. size沒變,或者額外size足夠,則不操作
2. 如果縮小,可以split
3. 如果next是free然後提供了足夠的空間大小,可以合併在split

void *Irealloc(void *p,size_t size){
    size_t s;
    p_block b,new;
    void *newp;
    if(!p)
        return (Imalloc(size));
    if(valid_addr(p)){
        s=align4(size);
        b=get_block(p);
        if(b->size>=s){
            if(b->size-s>=(META_DATA_SIZE+4))
                split_block(b,s);
        }else{
            if(b->next&&b->next->free&&
              (b->size+META_DATA_SIZE+b->next->size)>=s){
                fusion(b);
                if(b->size-s>=(META_DATA_SIZE+4))
                split_block(b,s);
            }else{
                newp=Imalloc(s);
                if(!newp)
                    return (NULL);
                new=get_block(newp);
                copy_block(b,new);
                    Ifree(p);
                return(newp);
            }
        }
        return (p);
    }
    return (NULL);
}

至此,已基本完成了malloc,free,realloc,calloc的功能,但也只是簡單粗略的實現,但也還有很多地方值得修改的,比如文章http://blog.jobbole.com/75656/描述的那樣:
1. 分配內存較大塊內存時,使用mmap替代sbrk
2. 維護多個鏈表,提高查找效率
也有後面需要做的就是http://www.skywind.me/blog/archives/1480中提到的一些功能和觀點:
1. 做好標記和保護,避免二次釋放
2. 修改查找算法,查找最合適大小的內存塊上
3. 夥伴算法
4. freelist
5. slab代替Freelist
6. 混合分配策略
7. 實現地址着色
8. 優化緩存競爭

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章