CSAPP-Lab6:MallocLab

目录

1、实验文档注释

2、实验题目

2.1 基本常数、宏、函数

2.2 函数

mm_init

extend_heap

coalesce

 mm_malloc

 first_fit

 best_fit

 place

 mm_free

 mm_realloc

3、总结


1、实验文档注释

在本实验中,你将为C语言程序编写一个动态存储分配器,即你自己版本的 malloc、free 和 realloc程序。我们鼓励你创造性地探索设计空间,并且 实现一个正确、高效和快速的分配器。

你的动态存储分配器将由以下四个函数组成,它们在 mm.h 中声明 中声明,并在 mm.c 中定义

int mm_init(void);
void *mm_malloc(size_t size);
void mm_free(void *ptr);
void *mm_realloc(void *ptr, size_t size);

2、实验题目

2.1 基本常数、宏、函数

/* 基本常数和宏 */
#define WSIZE 4    /* 字 */
#define DSIZE 8    /* 双字 */
#define CHUNKSIZE (1 << 12)    /* 初始空闲块和扩展堆的默认大小 */

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

/* 将块大小和已分配位结合为一个值,可以存在头部和脚部 */
/* 此处注意 size 的低 3 位一定是空的,因为大小是 8 的倍数 */
/* alloc 占第 0 位 */
#define PACK(size, alloc) ((size) | (alloc))

/* 读写指针 p 的位置 */
/* 读取和返回参数 p 引用的字 */
#define GET(p) (*(unsigned int *)(p))   
/* 必须类型转换,因为 p 是 void* */ 
#define PUT(p, val) (*(unsigned int *)(p) = (val))    

/* 头部或脚部调用,获取大小 */
#define GET_SIZE(p) (GET(p) & ~0x07)
/* 头部或脚部调用,获取分配位 */
#define GET_ALLOC(p) (GET(p) & 0x01)

/* 给定有效载荷,找到头部指针 */
#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)))

static void *extend_heap(size_t words);
static void place(void *bp, size_t asize);
static void *first_fit(size_t asize);
static void *best_fit(size_t asize);
static void *coalesce(void *bp);

2.2 函数

在调用 mm_malloc、mm_realloc 或 mm_free 之前,应用程序(即、 你将用来评估你的实现的跟踪驱动程序)调用mm_init来执行任何必要的初始化。执行任何必要的初始化,如分配初始堆区。返回值应该是 如果在执行初始化时有问题,返回值应为-1,否则为0。

mm_init函数从内存系统得到 4 个字,并将其初始化,创建一个空的空闲链表。然后调用 extend_heap 函数,用来扩展 CHUNKSIZE 字节,并创建初始的空闲块。

mm_init

int mm_init(void){
	//分配4 * 4大小
	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));	// 结尾块,大小为0,整个堆就一个
	// 有效载荷位置
	heap_listp += (2 * WSIZE);
	
	// 扩展空闲空间
	if(extend_heap(CHUNKSIZE / WSIZE) == NULL){
		return -1;
	}
    return 0;
}

extend_heap

static void *extend_heap(size_t words){
	char *bp;
	size_t size;
	
	// 确定拓展字节数并对齐
	size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
	
	// bp指向新分配内存起始位置,因为会覆盖前面,所以也就是有效载荷的位置
	if((long)(bp = mem_sbrk(size)) == -1){
		return NULL;
	}
	
	// 这里HDRP可以看出,其实头部序言块是占有了上一个的结尾块,依次前移了
	PUT(HDRP(bp), PACK(size, 0));	// 设置头部
	PUT(FTRP(bp), PACK(size, 0));	// 设置脚部
	PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));	// 新的结尾块
		
	// 合并空闲
	return coalesce(bp);
} 

coalesce

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(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);
	}
	return bp;
}

 mm_malloc

void *mm_malloc(size_t size)
{
	size_t asize;
	void *bp;
	size_t extendsize;
	
	if(size == 0) return NULL;
	
	// 加上开销字节,然后对齐, 最小大小是双字
	if(size <= DSIZE){
		asize = 2 * DSIZE;
	}
	else{
		asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
	}
	
	// 寻找合适的空闲块
	if((bp = first_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;
}

 first_fit

static void *first_fit(size_t asize){
	void *bp = heap_listp;
	size_t size;
	while((size = GET_SIZE(HDRP(bp))) != 0){
		if(size >= asize && !GET_ALLOC(HDRP(bp))){
			return bp;
		}
		bp = NEXT_BLKP(bp);
	}
	return NULL;
}

 best_fit

static void *best_fit(size_t asize){
	void *bp = heap_listp;
	size_t size;
	void *best = NULL;
	size_t minSize = 0;
	
	while((size = GET_SIZE(HDRP(bp))) != 0){
		
		if(size >= asize && !GET_ALLOC(HDRP(bp)) && (minSize == 0 || minSize > size)){
			best = bp;
			minSize = size;
			if(size == asize){
				return best;
			}
		}
		bp = NEXT_BLKP(bp);
	}
	return best;
}

 place

static void place(void *bp, size_t asize){
	size_t reSize = GET_SIZE(HDRP(bp)) - asize;
	if(reSize >= DSIZE){
		PUT(HDRP(bp), PACK(asize, 1));
		PUT(FTRP(bp), PACK(asize, 1));
		PUT(HDRP(NEXT_BLKP(bp)), PACK(reSize, 0));
		PUT(FTRP(NEXT_BLKP(bp)), PACK(reSize, 0));
	}
	else{
		PUT(HDRP(bp), PACK(GET_SIZE(HDRP(bp)), 1));
		PUT(FTRP(bp), PACK(GET_SIZE(HDRP(bp)), 1));		
	}
}

 mm_free

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

void *mm_realloc(void *ptr, size_t size){
	size_t asize, ptrSize;
	void *newBp;
	
	// 空的直接申请新的
	if(ptr == NULL){
		return mm_malloc(size);
	}
	// 改为0则相当于直接删
	if(size == 0){
		mm_free(ptr);
		return NULL;
	}
	
	// 对齐修改的大小
	if(size <= DSIZE){
		asize = 2 * DSIZE;
	}
	else{
		asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
	}
	
	// 看当前块前后是否有空白,有则合并然后看当前块大小
	newBp = coalesce(ptr);
	ptrSize = GET_SIZE(HDRP(newBp));
	// 先标记为使用状态
	PUT(HDRP(newBp), PACK(ptrSize, 1));
	PUT(FTRP(newBp), PACK(ptrSize, 1));
	
	// 检查是否有合并前面的块(合并后面的不用改起始位置),前移
	if(newBp != ptr){
		memcpy(newBp, ptr, GET_SIZE(HDRP(ptr)) - DSIZE);
	}
	
	// 当前块大小和目标块大小一样,直接返回
	if(ptrSize == asize){
		return newBp;
	}
	// 当前块比目标大,切割
	else if(ptrSize > asize){
		place(newBp, asize);
		return newBp;
	}
	// 当前块比目标小,拷贝到新的
	else{
		ptr = mm_malloc(asize);
		if(ptr == NULL){
			return NULL;
		}
		memcpy(ptr, newBp, ptrSize - DSIZE);
		mm_free(newBp);
		return ptr;
	}
}

3、总结

懵懵懂懂,请多指教!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
malloclab是CSAPP(Computer Systems: A Programmer's Perspective)教材中的一个实验项目,旨在帮助学生了解内存管理和动态内存分配的细节。 实验的主要任务是实现一个简单的动态内存分配器。这个内存分配器需要提供malloc、free和realloc函数的功能,来动态管理内存。实验中提供了一个基本的代码框架,学生需要在这个框架上完成具体的实现。 整个实验可以分为三个部分:分配器的初始化、分配和释放内存的处理。 在初始化部分,学生需要定义一个初始的堆,为其分配一块内存,并根据实验要求设置好堆的初始状态。 在分配内存的部分,学生需要实现malloc函数。这个函数接收一个参数(需要分配的字节数)并返回指向分配内存的指针。学生需要根据实验要求分配内存,并保证分配的内存块满足对齐和避免碎片化的要求。 在释放内存的部分,学生需要实现free函数。这个函数接收一个参数(指向待释放内存块的指针)并将该内存块标记为可用。学生需要根据实验要求处理不同的情况,比如释放合并相邻的空闲块。 此外,实验还有一些额外的要求,如实现realloc函数,处理内存使用情况的统计等。 通过完成malloclab实验,学生可以深入了解内存管理和动态内存分配的工作原理。这个实验还提供了一个实践机会,让学生亲自动手实现一个简单的内存分配器,从而更好地理解和掌握相关的概念和技术。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值