已經實現了自己的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. 優化緩存競爭