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