雖然在IMalloc.c實現了內存申請的工作,但是這比實際中malloc函數的功能差了很多。必須要將堆中申請的內存管理起來,才能更好地實現malloc函數以及後面的free和realloc函數。
本節內容主要介紹完整實現malloc的功能,可以用於內存塊管理。
需要給每個內存塊添加額外的信息(meta-data):內存塊的大小,下一個內存塊的地址,當前內存塊是否可用。meta-data應該在malloc分配的內存塊之前。C語言描述該數據結構如下
typedef struct s_block *p_block;
struct s_block
{
size_t size;
p_block next;
int free;
char data[1];//內存塊的第一個字節,長度沒有算在meta-data中,數組真是神奇的指針
};
在內存中struct是一塊連續的字段,在32位系統中,size_t爲4bytes,所以總共meta-data佔12bytes。
#define META_DATA_SIZE 12
尋找合適的block採用First fit策略:從頭開始,使用第一個足夠滿足分配空間的內存塊。另外一種方式是Best策略:從頭開始,遍歷所有塊,使用數據區大小大於size且差值最小的塊作爲此次分配塊。
在內存需要將分配的內存對齊,32位系統中需要4字節對齊,使用宏來使大小對齊4,位4的倍數
#define align4(x) (((((x)-1)>>2)<<2)+4)
first fit查找合適內存塊:
void *base=NULL;
p_block find_block(p_block *last,size_t size){
p_block b=base;
while(b&&!(b->free&&b->size>=size)){
*last=b;
b=b->next;
}
return (b);
}
內存塊不足時,擴展內存塊:
p_block extend_heap(p_block last,size_t s){
p_block b;
b=sbrk(0);
s=align4(s);
if(sbrk(META_DATA_SIZE+s)==(void*)-1)
return (NULL);
b->size=s;
b->next=NULL;
if(last)
last->next=b;
b->free=0;
return (b);
}
分裂內存塊,防止內存浪費:
p_block extend_heap(p_block last,size_t s){
p_block b;
b=sbrk(0);
s=align4(s);
if(sbrk(META_DATA_SIZE+s)==(void*)-1)
return (NULL);
b->size=s;
b->next=NULL;
if(last)
last->next=b;
b->free=0;
return (b);
}
重寫IMalloc函數
void *Imalloc(size_t size){
p_block b,last;
size_t s;
s=align4(size);
if(base){
last=base;
b=find_block(&last,s);
if(b){
//可以分裂內存塊
if((b->size-s)>=META_DATA_SIZE+4)
split_block(b,s);
b->free=0;
}else{
//沒有找到合適的內存塊
b=extend_heap(last,s);
if(!b)
return (NULL) ;
}
}else{
//第一次申請
b=extend_heap(NULL,s);
if(!b)
return (NULL) ;
base=b;
}
return (b->data);
}
gdb調試查看
int *p=(int*)Imalloc(sizeof(int)*2);
int *pp=(int*)Imalloc(sizeof(int)*1028);
分配的內容:查看p,第一字節內容爲data大小8bytes,第二字節內容爲下塊內容所在首地址,即pp所在塊的地址0x804b014,符合預期分配設想
(gdb) x/4a p-4
0x804affc: 0x0 0x8 0x804b014 0x0
(gdb) x/8a p
0x804b00c: 0x0 0x0 0x1010 0x0
0x804b01c: 0x0 0x0 0x0 0x0`