【CSAPP】malloc Lab

这个实验算是CSAPP配套实验里面比较难的一个了。不过大体框架还是和书上隐式链表分配一致的,但是很多细节需要注意。

首先要知道几种分配器执行搜索的算法(这里只给出特点,但要做到心中有他们的工作原理):

1.首次适配:较大块在后面,搜索时间变长。

2.下一次适配:速度最快。

3.最佳适配:存储器利用率最高。

4.分离适配,特殊情况包括伙伴系统,优点在于快速搜索和快速合并(大小相同,地址连续)。


本次实验采用的是分离适配算法,下面先勾画出这个实验的大致框架,包括以下内容:

1.首先要弄清楚我们构建的堆的整体结构:

18个空闲链表头

填充块

序言块

序言块

中间都是普通块

结尾块

2.其次是普通块的结构: 地址从小到大方向为Head到Foot

Head

Next

Prev

有效载荷

填充

Foot

3.几个重要函数的整体流程:

   ①int mm_init(void)包括调用了mem_sbrk  ->  void *extend_heap(size_t words)  ->  void *coalesce(void *bp)包括调用了void insert_list(void *bp)和void delete_list(void *bp)  

   ②void *mm_malloc(size_t size)至少申请2DSIZE堆空间  ->  void *find_fit(size_t asize)和void place(void *bp, size_t asize)

   或者  ->  void *extend_heap(size_t words)之后void place(void *bp, size_t asize)

   ③void mm_free(void *ptr)设置head和foot  -> void *coalesce(void *bp)

   ④void *mm_realloc(void *ptr, size_t size)其中size为0,相当于free,ptr为NULL,相当于malloc,memmove实现旧数据移动,包括缩小空间和扩大空间两种情况。

4.实验材料给出了一个mdriver文件作为测试,主要从以下两方面:

(1)空间利用率,权值0.6。比如分配100,200,100字节的空间,再释放200字节,再分配300字节空间:一种变化为100,200,100->100,(200),100,300空间;另一种可能变化100,,200,(100),100->100,300,100。显然后面一种利用率较高

(2)throughput生产率,权值0.4。以C标准库的malloc等函数为基准。


============以下是mm.c具体程序,每段都有注释=============================================================

/*
 *本程序使用了分离适配算法,维护18个空闲链表,其头指针存放在heap开始处,后面 *同样紧跟着对齐块,序言头,足,结尾块。
 *以下是每个空闲块的结构:
 *      ——————— 低位
 *      |    head     |
 *      ———————
 *      |    next     |
 *      ———————
 *      |    prev     |
 *      ———————
 *      |   有效载荷  |        
 *      ———————
 *      |     填充    |
 *      ———————
 *      |    foot     |
 *      ———————  高位
 */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>

#include "mm.h"
#include "memlib.h"

/* Basic constants and macros */
#define WSIZE 4 	/* Word and header/footer size (bytes) */
#define DSIZE 8 	/* Double word size (bytes) */
#define CHUNKSIZE (1<<12) 	/* Extend heap by this amount (bytes) */

#define MAX(x, y) ((x) > (y) ? (x) : (y))

/* Pack a size and allocated bit into  word */
#define PACK(size, alloc) ((size) | (alloc))

/* Read and write a word at address p */
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))

/* Read and write a pointer at address p */
#define GET_PTR(p) ((unsigned int *)(long)(GET(p)))
#define PUT_PTR(p, ptr) (*(unsigned int *)(p) = ((long)ptr))

/* Read the size and allocated fields from address p */
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)

/* Given block ptr bp, compute address of its header and footer */
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)

/* Given block ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))


/* 18个空闲链表的大小 */
#define SIZE1 (1<<4)
#define SIZE2 (1<<5)
#define SIZE3 (1<<6)
#define SIZE4 (1<<7)
#define SIZE5 (1<<8)
#define SIZE6 (1<<9)
#define SIZE7 (1<<10)  		
#define SIZE8 (1<<11)
#define SIZE9 (1<<12)
#define SIZE10 (1<<13)
#define SIZE11 (1<<14)
#define SIZE12 (1<<15)
#define SIZE13 (1<<16)
#define SIZE14 (1<<17)
#define SIZE15 (1<<18)
#define SIZE16 (1<<19)
#define SIZE17 (1<<20) 		

#define LISTS_NUM 18 		

/* Globe var */
static char *heap_listp;

/* 函数声明 */
static void *extend_heap(size_t words);
static void *coalesce(void *bp);
static void *find_fit(size_t asize);
static void place(void *bp, size_t asize);
static void insert_list(void *bp);
int getListOffset(size_t size);
void delete_list(void *bp);

/*********************************************************
 * NOTE TO STUDENTS: Before you do anything else, please
 * provide your team information in the following struct.
 ********************************************************/
team_t team = {
	/* Team name */
	"qzy",
	/* First member's full name */
	"qzy",
	/* First member's email address */
	"qzy@XXX.com",
	/* Second member's full name (leave blank if none) */
	"",
	/* Second member's email address (leave blank if none) */
	""
};

/* single word (4) or double word (8) alignment */
#define ALIGNMENT 8

/* rounds up to the nearest multiple of ALIGNMENT */
#define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7)


#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))

/* 
 * mm_init - initialize the malloc package.
 */
int mm_init(void)
{
    /*新建一个heap,包括18个空闲链表,和4个初始化块*/
    char *bp;
	int i;

	if ((heap_listp = mem_sbrk((LISTS_NUM + 4) * WSIZE)) == (void *)-1) {
		return -1;
	}
	PUT(heap_listp + LISTS_NUM * WSIZE, 0);
	PUT(heap_listp + (1 + LISTS_NUM) * WSIZE, PACK(DSIZE, 1));
	PUT(heap_listp + (2 + LISTS_NUM) * WSIZE, PACK(DSIZE, 1));
	PUT(heap_listp + (3 + LISTS_NUM) * WSIZE, PACK(0, 1));
    /*将18个链表头初始化都指向NULL*/
	for (i = 0; i < LISTS_NUM; i++) {
		PUT_PTR(heap_listp + WSIZE * i, NULL);
	}

	/* Extend the empty heap with a free block of CHUNKSIZE bytes */
	if ((bp = extend_heap(CHUNKSIZE / WSIZE)) == NULL) {
		return -1;
	}

	return 0;
}

/*
 * 扩展heap大小
 */
void *extend_heap(size_t words)
{
	char *bp;
	size_t size;

	/* Allocate an even number of words to maintain alignment */
	size = (words % 2) ? ((words + 1) * WSIZE) : (words * WSIZE);
	if ((long)(bp = mem_sbrk(size)) == -1) {
		return NULL;
	}

	/* Initialize free block header/footer and the epilogue header */
	PUT(HDRP(bp), PACK(size, 0)); 		/* Free block header */
	PUT(FTRP(bp), PACK(size, 0)); 		/* Free block footer */
	PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); 	/* New epilogue header */

	/* Coalesce if the previous block was free */
	return coalesce(bp);
}


/*
 * 合并空闲块。合并的情况要先删除空闲块再插入。
 */
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) { 	/* 前后无空闲  */
		bp = bp;
	} else if (prev_alloc && !next_alloc) { 	/* 后空闲 */
		delete_list(NEXT_BLKP(bp));
		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) { 	/* 前空闲 */
		delete_list(PREV_BLKP(bp));
		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 { 				/* 前后均空闲 */
		delete_list(NEXT_BLKP(bp));
		delete_list(PREV_BLKP(bp));
		size = size + GET_SIZE(HDRP(PREV_BLKP(bp))) + 
			GET_SIZE(HDRP(NEXT_BLKP(bp)));
		PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
		PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
		bp = PREV_BLKP(bp);
	}

	insert_list(bp);
	return bp;
}


/*
 * 将一个空闲块插入到合适位置,插入位置为每个链表表头
 */
void insert_list(void *bp)
{
	int index;
	size_t size;
	size = GET_SIZE(HDRP(bp));
	index = getListOffset(size);
    /*当前链表表头指向NULL*/
	if (GET_PTR(heap_listp + WSIZE * index) == NULL) {
		PUT_PTR(heap_listp + WSIZE * index, bp);
		PUT_PTR(bp, NULL);
		PUT_PTR((unsigned int *)bp + 1, NULL);
	} else {    /*当前链表已经有元素,插入到该链表表头,新的第一个元素与原来第一个元素连接*/
		PUT_PTR(bp, GET_PTR(heap_listp + WSIZE * index));
		PUT_PTR(GET_PTR(heap_listp + WSIZE * index) + 1, bp);  	
        PUT_PTR((unsigned int *)bp + 1, NULL);
		PUT_PTR(heap_listp + WSIZE * index, bp);
	}
}
/* 
 * 删除链表结点,就是双向链表的删除操作。一共四种情况。
 */
void delete_list(void *bp)
{
    int index;
    size_t size;
    size = GET_SIZE(HDRP(bp));
    index = getListOffset(size);
    if (GET_PTR(bp) == NULL && GET_PTR((unsigned int *)bp + 1) == NULL) { 
        /* 后继next为NULL,前驱prev也为NULL,表明这个链表仅含一个结点。
        然后我们将合适大小对应的头指针设置为NULL*/
        PUT_PTR(heap_listp + WSIZE * index, NULL);
    } else if (GET_PTR(bp) == NULL && GET_PTR((unsigned int *)bp + 1) != NULL) {
        /*当前链表有多个结点,是最后一个。
        通过prev指针得到前一个块,再减去(unsigned int)1,就得到了指向next的指针,
        再将next指向NULL*/
        PUT_PTR( (GET_PTR( (unsigned int*)GET_PTR((unsigned int *)bp + 1) - 1 )), NULL );
        PUT_PTR(GET_PTR((unsigned int *)bp + 1), NULL);  //bp前驱指针prev=NULL
    } else if (GET_PTR(bp) != NULL && GET_PTR((unsigned int *)bp + 1) == NULL){
        /*当前链表有多个结点,是第一个
        第一条语句将相应大小的头指针指向了bp的next*/
        PUT_PTR(heap_listp + WSIZE * index, GET_PTR(bp));
        PUT_PTR(GET_PTR(bp) + 1, NULL); //prev=NULL
    } else if (GET_PTR(bp) != NULL && GET_PTR((unsigned int *)bp + 1) != NULL) {
        /*当前链表有多个节点,为中间结点
        第一条前一个块的next指向了当前块的next*/
        PUT_PTR(GET_PTR((unsigned int *)bp + 1), GET_PTR(bp));
        PUT_PTR(GET_PTR(bp) + 1, GET_PTR((unsigned int*)bp + 1));//bp->prev = bp->next
    }
}


/*
 * 大小为size的块取哪个空闲链表中到块最合适 */
int getListOffset(size_t size)
{
	if (size <= SIZE1) {
		return 0;
	} else if (size <= SIZE2) {
		return 1;
	} else if (size <= SIZE3) {
		return 2;
	} else if (size <= SIZE4) {
		return 3;
	} else if (size <= SIZE5) {
		return 4;
	} else if (size <= SIZE6) {
		return 5;
	} else if (size <= SIZE7) {
		return 6;
	} else if (size <= SIZE8) {
		return 7;
	} else if (size <= SIZE9) {
		return 8;
	} else if (size <= SIZE10) {
		return 9;
	} else if (size <= SIZE11) {
		return 10;
	} else if (size <= SIZE12) {
		return 11;
	} else if (size <= SIZE13) {
		return 12;
	} else if (size <= SIZE14) {
		return 13;
	} else if (size <= SIZE15) {
		return 14;
	} else if (size <= SIZE16) {
		return 15;
	} else if (size <= SIZE17) {
		return 16;
	} else {
		return 17;
	}
}


/* 
 * mm_malloc - Allocate a block by incrementing the brk pointer.
 *     Always allocate a block whose size is a multiple of the alignment.
 */
void *mm_malloc(size_t size)
{
	size_t asize; 		/* Adjusted block size */
	size_t extendsize; 	/* Amount to extend heap if no fit */
	char *bp;

	/* Igore spurious requests */
	if (0 == size) {
		return NULL;
	}

	/* Adjusted block size to include overhead and alignment reqs */
	if (size <= DSIZE) {
		asize = 2 * DSIZE;
	} else {
		asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
	}

	/* Search the free list for a fit */
	if ((bp = find_fit(asize)) != NULL) {
		place(bp, asize);
		return bp;
	}

	/* No fit found. Get more memory and place the block */
	extendsize = MAX(asize, CHUNKSIZE);
	if ((bp = extend_heap(extendsize / WSIZE)) == NULL) {
		return NULL;
	}
	place(bp, asize);
	return bp;
 }

/*
 * 根据大小先确定哪一个空闲链表所含空闲块最合适。
 */
void *find_fit(size_t asize)
{
	int index;
	index = getListOffset(asize);
	unsigned int *ptr;

	/* 最合适的空闲链表没有满足要求到空闲块就到下一个空闲链表寻找*/
	while (index < 18) {
		ptr = GET_PTR(heap_listp + 4 * index);
		while (ptr != NULL) {
			if (GET_SIZE(HDRP(ptr)) >= asize) {
				return (void *)ptr;
			}
			ptr = GET_PTR(ptr);
		}
		index++;
	}

	return NULL;
}

/*
 * 确定新分配的空闲块是否需要分割。
 */
void place(void *bp, size_t asize)
{
	size_t csize = GET_SIZE(HDRP(bp));
	delete_list(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));
		insert_list(bp);
	} else {
		PUT(HDRP(bp), PACK(csize, 1));
		PUT(FTRP(bp), PACK(csize, 1));
	}
}

/*
 * mm_free - Freeing a block does nothing.
 */
void mm_free(void *ptr)
{	
    size_t size = GET_SIZE(HDRP(ptr));

	PUT(HDRP(ptr), PACK(size, 0));
	PUT(FTRP(ptr), PACK(size, 0));
	coalesce(ptr);
}


/*
 * mm_realloc - 直接用malloc和free组合实现
 */
void *mm_realloc(void *ptr, size_t size)
{
	size_t asize;
	void *oldptr = ptr;
	void *newptr;

	/* free */
	if (0 == size) {
		free(oldptr);
		return NULL;
	}


	if (size <= DSIZE) {
		asize = 2 * DSIZE;
	} else {
		asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
	}

	if (asize == GET_SIZE(HDRP(oldptr))) {
		return oldptr;
	}
	
	/* 缩小空间 */
	if (asize < GET_SIZE(HDRP(oldptr))) {
		newptr = mm_malloc(size);
		memmove(newptr, oldptr, size);
		mm_free(oldptr);

		return newptr;
	}

	/* 从heap的其他地方寻找 */
	newptr = mm_malloc(size);
	if (NULL == newptr)
		return NULL;
	memmove(newptr, oldptr, size);
	mm_free(oldptr);

	return newptr;
}


在官网下载的handout,解压后有两个tracefile:short1-bal.rep和short2-bal.rep。调用以下命令进行性能分析,即可输出结果:
make
./mdriver -f short1-bal.rep

经过测试,此程序两个tracefile的结果分别是:39+40 = 79/100                    53+40 = 93/100

并且对比书中隐式空闲链表分配方式:              40+31 = 71/100                    54+28  = 82

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值