首先,先了解下书中所介绍的采用隐式空闲链表的简单适配器实现贴出来供大家参考,在Ubandu下编译成功。
#include
#include
#define WSIZE 4
#define DSIZE 8
#define CHUNKSIZE (1<<12)
#define MAX(x,y) ((x)>(y)?(x):(y))
#define PACK(size,alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p))
#define PUT(p,val) (*(unsigned int *)(p) = (val))
#define GETSIZE(p) (GET(p)& ~0x7)
#define GETALLOC(p) (GET(p) & 0x1)
#define HDRP(p) ((char*)(p)-WSIZE)
#define FTRP(p) ((char*)(p) + GETSIZE(HDRP(p)) - DSIZE )
#define NEXTBLCK(p) ((char*)(p) + GETSIZE(HDRP(p)))
#define PREVBLCK(p) ((char*)(p) - GETSIZE( ((char*)(p) - DSIZE)))
static char* heap_listp;
static char* last_listp;
static void* coaleace(void* bp){
size_t nextAlloc = GETALLOC(HDRP(NEXTBLCK(bp)));
size_t prevAlloc = GETALLOC(FTRP(PREVBLCK(bp)));
size_t size = GETSIZE(HDRP(bp));
if(nextAlloc && prevAlloc)
return bp;
else if(!nextAlloc && prevAlloc){
size = size + GETSIZE (HDRP(NEXTBLCK(bp)));
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
return bp;
}else if(nextAlloc && !prevAlloc){
size = size + GETSIZE( FTRP(PREVBLCK(bp)));
bp = PREVBLCK(bp);
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
return bp;
}else if(!nextAlloc && !prevAlloc){
size = size + GETSIZE (HDRP(NEXTBLCK(bp))) +GETSIZE( FTRP(PREVBLCK(bp)));
bp = PREVBLCK(bp);
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
return bp;
}
}
static void* newCoaleace(void *bp){
//对于下一个块的合并与原来一样,不再赘述。只考虑前一个块的合并问题
char * tempBp = NEXTBLCK(heap_listp);
//bp是heap_listp后的第一个块,无须合并前一个,直接返回。
if(tempBp ==(char*)bp)
return bp;
//找到bp的前一个块
while(NEXTBLCK(tempBp)!=(char*)bp){
tempBp = NEXTBLCK(tempBp);
}
size_t prevAlloc = GETALLOC(HDRP(tempBp));
//剩下的合并操作类似于书中介绍。
}
static void* extend_heap(size_t words){
char* bp;
size_t size;
size = (size%2)?(words+1)*WSIZE:words*WSIZE;
if((long)(bp = (char*)sbrk(size))==-1)
return NULL;
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
PUT(HDRP(NEXTBLCK(bp)),PACK(0,1));
return coaleace(bp);
}
int mm_init(){
heap_listp = (char*)sbrk(4*WSIZE);
if(heap_listp==(void*)-1)
return -1;
PUT(heap_listp,0);
PUT(heap_listp+WSIZE,PACK(DSIZE,1));
PUT(heap_listp+2*WSIZE,PACK(DSIZE,1));
PUT(heap_listp+3*WSIZE,PACK(0,1));
heap_listp = heap_listp + 2*WSIZE;
if(extend_heap(CHUNKSIZE/WSIZE)==NULL)
return -1;
}
void* mm_free(void* bp){
size_t size = GETSIZE(HDRP(bp));
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
return coaleace(bp);
}
static void *find_fit(size_t asize){
char* bp = NEXTBLCK(heap_listp);
while(GETSIZE(HDRP(bp))!=0){
if(GETSIZE(bp)>=asize && !GETALLOC(bp))
return bp;
}
return NULL;
}
static void* find_nextFit(size_t asize){
char* bp = NEXTBLCK(last_listp);
while(GETSIZE(HDRP(bp))!=0){
if(GETSIZE(bp)>=asize && !GETALLOC(bp))
return bp;
}
return NULL;
}
static void place(void *bp, size_t asize){
size_t size = GETSIZE(HDRP(bp));
if(size >= 2*DSIZE +asize){
PUT(HDRP(bp),PACK(asize,1));
PUT(FTRP(bp),PACK(asize,1));
PUT(HDRP(NEXTBLCK(bp)),PACK(size-asize,0));
PUT(FTRP(NEXTBLCK(bp)),PACK(size-asize,0));
}else{
PUT(HDRP(bp),PACK(size,1));
PUT(FTRP(bp),PACK(size,1));
}
}
void* mm_malloc(size_t size){
size_t asize ;
size_t extendsize;
char* bp;
if(size==0)
return NULL;
if(size<=DSIZE)
asize = 2* DSIZE;
else{
asize =size+(DSIZE-size%DSIZE) + DSIZE;
}
if((bp=(char*)find_fit(asize))!=NULL){
place(bp,asize);
return bp;
}
extendsize = MAX(asize,CHUNKSIZE);
if((bp = (char*)extend_heap(extendsize/WSIZE))==NULL)
return NULL;
place(bp,asize);
return bp;
}
int main(){
mm_init();
exit(0);
}
在只有一个空闲链表的情况下:
进行首次适配的时间不需要所有块的线性时间,仅仅需要空闲块的线性时间,并且注意如果发生分割,需要更新pred和succ
当归还一个分配的块的时候,我们可以根据头部和脚部判断是否可以进行合并,注意合并时候需要更新块的pred和succ。
这里简单写下合并时候的代码思想如下:
static void* newCoaleace1(void* bp){
size_t nextAlloc = GETALLOC(HDRP(NEXTBLCK(bp)));
size_t prevAlloc = GETALLOC(FTRP(PREVBLCK(bp)));
size_t size = GETSIZE(HDRP(bp));
//如果发现不能合并,简单的将bp连接到空闲链表头部,并且更新bp的pred和succ指针
if(nextAlloc && prevAlloc){
SUCC(bp) = freeHead;
PRED(bp) = 0;
return bp;
}
//如果发现一边可以合并,假设后边的可以合并,前边的合并相同,遂省略
else if (!nextAlloc && prevAlloc){
SUCC(bp) = SUCC(NEXTBLCK(bp));
PRED(SUCC(bp)) = bp;
PRED(bp) = PRED(NEXTBLCK(bp));
SUCC(PRED(bp)) = bp;
//更改头部和脚部代码与上面相同
}
//如果发现两边都可以合并,那么直接把左右两边从原来的链表中删除后,将合并的块放到空闲链表头部
else if(!nextAlloc && prevAlloc){
//链表操作同上,更改头部脚部代码同上
}
}
最后,回归主题,分离适配维护了多个空闲链表。这些空闲链表每个链表有固定的块大小。(16 ,32,64......)
1.当进行分配时,先选择刚好满足分配空间的空闲链表,假设为32. 如果此时32空闲链表中没法满足需求,则到64空闲链表中寻找,直到找出一个满足分配空间的大小,否则就申请一个新空间。
2.分配时,判断是否需要分块。如果在64空闲链表中发生了分配,如果剩余块大小满足某链表(假设为16),则将该块链接到16的头部。
3.归还时,需要判断是否可以合并。如果可以合并,不同于只有一个空闲链表的情况,需要判断合并后的大小来判断将其链接到哪个空闲链表。
其实,分离适配的难点就是需要将块链接到不同大小的空闲链表。只要维护好块大小数据,其代码与只有一个空闲链表的情况是类似的。
最最后~~如果我的想法有错误,烦请指正!