已经实现了自己的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. 优化缓存竞争