@windmelon
2018-11-30T01:58:32.000000Z
字数 4768
阅读 4324
csapp实验
malloclab顾名思义就是要求自己实现c语言中的malloc函数,此外还有free和realloc函数
这个实验是csapp系列实验目前做到现在觉得最棘手的了=_=|||
可能是因为这部分上课没有认真听,参考了几个博客之后,终于理解了,先尝试完成一个最简单的隐式空闲链表+首次适配+原始realloc的版本,这个版本书上基本都给出了完整的代码,我们只需要实现find_fit()和place()即可
首先,先在mm.h中把需要的宏定义和函数声明加上
#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 GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
extern void *extend_heap(size_t words);
extern void *coalesce(void *bp);
extern void *find_fit(size_t size);
extern void place(void *bp,size_t asize);
extern int mm_init (void);
extern void *mm_malloc (size_t size);
extern void mm_free (void *ptr);
extern void *mm_realloc(void *ptr, size_t size);
然后是mm.c里面各个函数的实现
不要忘记新建一个指向块首的指针,在遍历整个堆寻找合适块的时候要用到
static char *heap_listp = 0;
以堆底为起始位置,新建四个WSIZE(4bytes or 32bits)大小的块
第一块什么都不放作为填充字
第二、三块分别作为序言块的头和脚
第四块作为结尾块
然后将指针后移两块,指向序言块脚部起始位置
随后调用extend_heap(),申请CHUNKSIZE大小的空间,以备用
int mm_init(void)
{
if((heap_listp = mem_sbrk(4*WSIZE))==(void *)-1){
return -1;
}
PUT(heap_listp,0);
PUT(heap_listp+(1*WSIZE),PACK(DSIZE,1));
PUT(heap_listp+(2*WSIZE),PACK(DSIZE,1));
PUT(heap_listp+(3*WSIZE),PACK(0,1));
heap_listp += (2*WSIZE);
if(extend_heap(CHUNKSIZE/WSIZE)==NULL){
return -1;
}
return 0;
}
此函数将堆扩容指定byte大小
如果指定words大小不为8的倍数则向上取整使得每次扩容都是八字节对齐
最后将头、脚内容补齐
并将下一个块置为结尾块
最后调用coalesce()函数堆bp进行合并操作后返回
static void *extend_heap(size_t words){
char *bp;
size_t size;
size = (words%2) ? (words+1)*WSIZE : words*WSIZE;
if((long)(bp=mem_sbrk(size))==(void *)-1)
return NULL;
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
PUT(HDRP(NEXT_BLKP(bp)),PACK(0,1));
return coalesce(bp);
}
此函数申请大小为size的空间
asize为对size进行8字节对齐检查后的大小
extendsize为取CHUNKSIZE和asize中较大者
先用find_fit()在现有的块中进行搜索,如果搜索到了,即用place()函数将asize大小的空间放到bp中(有可能全部分也可能分割成一个占用块和一个空闲块,所以不能粗暴的完全占用,要写一个place()函数来分配)
如果找不到合适块,就向堆申请新空间,新空间的大小为extendsize,然后再用place()函数放入
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 = (DSIZE)*((size+(DSIZE)+(DSIZE-1)) / (DSIZE));
}
if((bp = find_fit(asize))!= NULL){
place(bp,asize);
return bp;
}
extendsize = MAX(asize,CHUNKSIZE);
if((bp = extend_heap(extendsize/WSIZE))==NULL){
return NULL;
}
place(bp,asize);
return bp;
}
此函数释放指针指向的占用块
很简单,将块的头尾块的alloc信息改为0
然后进行合并
void mm_free(void *bp)
{
if(bp == 0)
return;
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
coalesce(bp);
}
此函数将对传入的块指针进行前后检查
当然,传进来的是空闲块
如果前面块或者后面块同样为空闲块,就进行合并
说起来很简单,代码还是比较长,需要认真看看
首先获取前后块的空闲状态,然后进行条件判断
四种情况
1.前后都忙碌。2.前忙碌,后空闲。3.前空闲,后忙碌。4.前后都空闲
static void *coalesce(void *bp){
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp)));
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
if(prev_alloc && next_alloc) {
return bp;
}else if(prev_alloc && !next_alloc){
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size,0));
PUT(FTRP(bp), PACK(size,0));
}else if(!prev_alloc && next_alloc){
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp),PACK(size,0));
PUT(HDRP(PREV_BLKP(bp)),PACK(size,0));
bp = PREV_BLKP(bp);
}else {
size +=GET_SIZE(FTRP(NEXT_BLKP(bp)))+ GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(NEXT_BLKP(bp)),PACK(size,0));
PUT(HDRP(PREV_BLKP(bp)),PACK(size,0));
bp = PREV_BLKP(bp);
}
return bp;
}
此函数从头至尾遍历堆,找到合适大小的块则返回指向该块的指针(即头部块末尾)
找不到则返回NULL
static void *find_fit(size_t size){
void *bp;
for(bp = heap_listp; GET_SIZE(HDRP(bp))>0; bp = NEXT_BLKP(bp)){
if(!GET_ALLOC(HDRP(bp)) && (size <= GET_SIZE(HDRP(bp)))){
return bp;
}
}
return NULL;
}
此函数将经过8字节对齐大小的空间放到指定的块中
如果分割后原块剩余大小大于2*DSIZE即16bytes
则进行分块
否则将整个块都占用
static void place(void *bp,size_t asize){
size_t csize = GET_SIZE(HDRP(bp));
if((csize-asize)>=(2*DSIZE)){
PUT(HDRP(bp),PACK(asize,1));
PUT(FTRP(bp),PACK(asize,1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp),PACK(csize-asize,0));
PUT(FTRP(bp),PACK(csize-asize,0));
}else{
PUT(HDRP(bp),PACK(csize,1));
PUT(FTRP(bp),PACK(csize,1));
}
}
此函数返回指向一个大小为size的区域指针,满足以下条件:
if ptr is NULL, the call is equivalent to mm_malloc(size);
if size is equal to zero, the call is equivalent to mm_free(ptr);
if ptr is not NULL:先按照size指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来ptr所指内存区域
void *mm_realloc(void *ptr, size_t size)
{
size_t oldsize;
void *newptr;
/* If size == 0 then this is just free, and we return NULL. */
if(size == 0) {
mm_free(ptr);
return 0;
}
/* If oldptr is NULL, then this is just malloc. */
if(ptr == NULL) {
return mm_malloc(size);
}
newptr = mm_malloc(size);
/* If realloc() fails the original block is left untouched */
if(!newptr) {
return 0;
}
/* Copy the old data. */
oldsize = GET_SIZE(HDRP(ptr));
if(size < oldsize) oldsize = size;
memcpy(newptr, ptr, oldsize);
/* Free the old block. */
mm_free(ptr);
return newptr;
}
至此,一个简单的隐式空闲链表+首次适配+原始realloc版本的malloclab就完成了