Lab5 Malloc Lab

Lab5 Malloc Lab

CSAPP Lab6 实验记录 ---- Malloc Lab(全实验流程 + 85 Scores)_malloclab实验-CSDN博客

CSAPP(CMU 15-213):Lab6 Malloclab详解_csapp malloc lab-CSDN博客

这两篇可以

前言

这是篇废稿,因为自己写崩了,就真写不出来。然后有价值的就前面两篇推荐,后面就别看了。我就把隐式空闲单向链表照猫画虎地加了注释,后面的都没弄完。但不想做了。

发这篇就相当于留个爪。

一、前置

1.目的

编写一个动态内存申请器,即在mm.c中完成或修改四个函数。

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);

使用不同方法改变利用率和吞吐率,逐步优化。

通过课程学习:实现方法有有Implicit list、Explicit list、Segregated list;然后是 fit algorithm,有Best fit、first fit、next fit

相关命令,基本前两条就够了

unix> make			// 构建驱动程序
unix> ./mdriver -V -f short1-bal.rep	// 在一个小型测试跟踪文件上运行驱动程序
unix> ./mdriver -h  // 获取驱动程序标志列表

在这里插入图片描述

查资料发现是trace不完整,然后网上博客有讲下载,就前面觉得可以的那一篇就行。

在这里插入图片描述

valid: 表示内存分配器实现是否通过了该测试案例。

util: 表示内存利用率,即分配的内存使用率。

ops: 表示在测试案例中执行的内存操作次数。

secs: 表示执行测试案例所花费的时间,单位是秒。

Kops: 表示每秒执行的内存操作数(千次操作数,K为千的意思)。

分数有util和thru(吞吐量)两个确定

二、编写

原先的方法

/*
 * mm-naive.c - 最快但内存利用效率最低的malloc包。
 * 
 * 在这种简单的实现中,通过简单地增加brk指针来分配块。
 * 每个块只是纯粹的数据负载。没有头部或尾部信息。
 * 块永远不会合并或重用。realloc直接使用mm_malloc和mm_free实现。
 */

(1)隐式空闲单向链表+first fit

思路:①先考虑单个块结构,为了实现方便,空闲块和已分配的块都有头和脚标记,考虑这个结构,发现其实不用额外定义,在malloc时额外申请两个字就行②然后是整体的链表,根据课上的图,肯定专门要有一个root和一尾巴。然后就是初始化对齐块③然后是实现malloc、free、relloc

①宏定义,简化操作

/* 已有宏,分别是对齐大小、对申请大小进行对齐(比如13会被对齐到16)、SIZE_T_SIZE为size_t类型对齐后大小(比如int实际字节) */
#define ALIGNMENT 8
#define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7)
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))

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

/* 自定义宏 */
// 状态
#define WSIZE    4        //字、脚部或头部的大小
#define DSIZE    8        //双字大小(字节)
#define CHUNKSIZE  (1<<12)   // 扩展堆时的默认大小
#define MINBLOCK (DSIZE + 2*WSIZE)	// 最小块大小:对齐+tag

// 操作p为指针
#define PACK(size, alloc)  ((size) | (alloc))	// 分配时已分配的a为1
#define GET(p)	(*(unsigned int *)(p))		// 读取p这个地方的一个字
#define PUT(p, val)   (*(unsigned int *)(p) = (val))	// 向p这个地方写一个字
#define GET_SIZE(p)  (GET(p) & ~0x07) // 得到p的size
#define GET_ALLOC(p) (GET(p) & 0x1) // 得到p的分配bit

// 操作 bp为指向有效载荷块的指针.char*可兼容
#define HDRP(bp)     ((char*)(bp) - WSIZE)  //获得头部的地址
#define FTRP(bp)     ((char*)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)  //获得脚部的地址, 与宏定义HDRP有耦合
#define NEXT_BLKP(bp)    ((char*)(bp) + GET_SIZE((char*)(bp) - WSIZE))  //计算后块的地址
#define PREV_BLKP(bp)    ((char*)(bp) - GET_SIZE((char*)(bp) - DSIZE))  //计算前块的地址

全局变量和辅助函数

static void* heap_listp;	// 指向序言块
/* private functions */
static void *extend_heap(size_t size);     //拓展堆块
static void *find_fit(size_t size);        //寻找空闲块
static void place(char *bp, size_t size);  //分割空闲块
static void *coalesce(void *bp);           //合并空闲块

②初始化链表

在这里插入图片描述

plus:如图,一个块为4字节(32为),因为头+尾,两个块为最小块,所以最开始的序言块8/0哪里都是占满了,所以直接用8/1

int mm_init(void)
{
	// 先申请一个头和一个尾,按最小算,一共4*WSIZE
	if((heap_listp=mem_sbrk(4*WSIZE))==(void*)-1)
		return -1;
	PUT(heap_listp,0);	// 为了对齐,第一个字不用
	// 第2、3个字为序言块,分别为头、有效载荷
	PUT(heap_listp+1*WSIZE,PACK(8,1));
	PUT(heap_listp+2*WSIZE,PACK(8,1));
	// 最后一个字为结尾0/1
	PUT(heap_listp+3*WSIZE,PACK(0,1));
	
	heap_listp += (2*WSIZE);	// 技巧,指向序言块中间
	
	// 看堆还能是否再申请
	if(extend_heap(CHUNKSIZE)==NULL)
		return -1;
}

③malloc操作

// 申请大小为size的块
void *mm_malloc(size_t size)
{
	size_t asize;			// 加上tag后的块大小
	size_t extendsize;
	void *bp = NULL;

	if(size==0)	return NULL;
	asize = ALIGN(size + 2*WSIZE);	// 头+尾,然后对齐
	
	// 刚好有一块大小够
	if((bp = find_fit(asize))!=NULL)
	{
		place((char *)bp,asize);
		return bp;
	}
	
	// 没有合适的大小空闲块
	extendsize = MAX(asize,CHUNKSIZE);
	if((bp = extend_heap(extendsize)) == NULL)
		return NULL;
	place(bp,asize);
	return bp;
}

④free操作

void mm_free(void *ptr)
{
	size_t size = GET_SIZE(ptr);
	PUT(HDRP(ptr),PACK(size,0));
	PUT(FTRP(ptr),PACK(size,0));
	coalesce(ptr);
}

⑤relloc操作

void *mm_realloc(void *ptr,size_t size)
{
	size_t old_size, new_size, extendsize;
	void *old_ptr, *new_ptr;
	
	// 特殊情况
	if(ptr==NULL)
		return mm_malloc(size);
	if(size == 0)
	{
		mm_free(ptr);
		return NULL;
	}
	
	old_size = GET_SIZE(HDRP(ptr));
	old_ptr = ptr;
	new_size = ALIGN(size + 2*WSIZE);
	// 比原来大or小
	if(old_size>=new_size)	// 比原来小,就分割
	{
		if(old_size-new_size>=MINBLOCK)
		{
			place(old_ptr,new_size);
		}else
		{
			return old_ptr;
		}
	}else		// 比原来大,就释放然后再寻找
	{
		// 如果找不到适配,扩展堆;找到new_ptr
		if((new_ptr = find_fit(new_size))==NULL)
		{
			extendsize = MAX(new_size,CHUNKSIZE);
			if((new_ptr = extend_heap(extendsize)) == NULL)	
			return NULL;
		}
		place(new_ptr,new_size);
		// 复制块内容
		memcpy(new_ptr,old_ptr,old_size-2*WSIZE);
		mm_free(old_ptr);
		
		return new_ptr;
	}
}

⑥辅助函数的实现

前面四个函数的实现抽象了一些很重要的步骤

// 首次适配,从头开始搜索
static void *find_fit(size_t size)
{
	void *curbp;
	for(curbp = heap_list;GET_SIZE(HDRP(curbp)) > 0;curbp = NEXT_BLKP(curbp))
	{
		if(!GET_ALLOC(HDRP(curbp))&&(GET_SIZE(HDRP(curbp)) >= size))
		return curbp;
	}
	
	return NULL;	// 未适配
}
// 分割
// 只要在asize的头和尾打上标记就行,但要注意最小块,就如果空余的比最小块还小,就直接整块算上,否则就分开标
static void place(char *bp,size_t asize)
{
	size_t total_size = GET_SIZE(HDRP(bp));
	size_t left_size = total_size - asize;
	
	if(left_size >= MINBLOCK)
	{
		PUT(HDRP(bp),PACK(asize,1));
		PUT(FTRP(bp),PACK(asize,1));
		bp = NEXT_BLKP(bp);
		PUT(HDRP(bp),PACK(left_size,0));
		PUT(FTRP(bp),PACK(left_size,0));
	}else
	{
		PUT(HDRP(bp),PACK(totoal_size,1));
		PUT(FTRP(bp),PACK(totoal_size,1));
	}
	
}
// 合并内存块
// 核心思想是后块就增加size大小,前块就前移bp,最后打上标记就行
static void *coalesce(void *bp)
{
	int pre_alloc  = GET_ALLOC(HDRP(PREV_BLKP(bp)));
	int post_alloc = GET_ALLOC(HDPR(NEXT_BLKP(bp)));
	size_t size = GET_SIZE(HDRP(bp));
	
	if(pre_alloc&&post_alloc)
	{
		return bp;
	}
	
	if(!post_alloc)
		size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
	if(!pre_alloc)
	{
		size += GET_SIZE(HDRP(PREV_BLKP(bp)));
		bp = PREV__BLKP(bp);
	}
	
	
	PUT(HDRP(bp),PACK(size,0));
	PUT(FTRP(bp),PACK(size,0));

	return bp;
}
// 将堆扩大size
static void *exetend_heap(size_t size)
{
	// asize为请求大小对齐处理,然后用mem_sbrk来扩展堆
	size_t asize;
	void *bp;
	asize = ALIGN(size);
	
	if((long)(bp = mem_sbrk(asize))== -1)
		return NULL;
	
	// 设置新分配块的头部和尾部
	PUT(HDRP(bp),PACK(asize,0));
	PUT(FTRP(bp),PACK(asize,0));
	
	// 设置新结尾块的头部
	PUT(HDRP(NEXT_BLKP(bp)),PACK(0,1));
	
	// 对新分配的块进行合并操作
	return coalesce(bp);
}

分析

在这里插入图片描述

(2)显式空闲链表+first fit

思路:①前面隐式空闲列表是通过大小相对位置跳转,而显示空闲列表是则在空闲头部块下面加了Next和Prev指向前面或者后面一个空闲块。已分配块还是和原来一样。所以增加空闲块内的两指针。

在这里插入图片描述

②然后就是整条链表,是空闲块链起来。root和尾巴,然后相关操作更新就行了。

整个思路在原来的隐式空闲链表上改一下就行了

①宏定义

#define MINBLOCK (DSIZE + 2*WSIZE + 2*WSIZE)
// 得到地址
#define GETADDR(p)  (*(unsigned int **)(p))
// 改变存的地址
#define PUTADDR(p,addr)	 (*(unsigned int **)(p) = (unsigned int *)(addr))
// 指向祖先指针的指针
#define PRED_POINT(bp)	(bp)
//指向后继指针的指针
#define SUCC_POINT(BP)  ((char*)(bp) + WSIZE)

static void* head_free;

static void insert_freelist(void *bp); 使用头插法向空闲链表中插入空闲块
static void remove_freelist(void *bp); 从空闲链表中移除空闲块,合并中使用(合并策略)
static void place_freelist(void *bp); 对空闲链表中的空闲块进行前部分割(分割策略)
static void *find_fit(size_t size); 针对链表进行搜索上的修改

三、总结

2.关于调试

#define VERBOSE 0
#ifdef DEBUG
#define VERBOSE 1
#endif

static void mm_printblock(int verbose, const char* func) {
    if (!verbose) return;
    char *curbp;
    printf("\n=========================== %s ===========================\n" ,func);
    for (curbp = heap_listp; GET_SIZE(HDRP(curbp)) > 0; curbp = NEXT_BLKP(curbp)) {
        printf("address = %p\n", curbp);
        printf("hsize = %d, fsize = %d\n", GET_SIZE(HDRP(curbp)), GET_SIZE(FTRP(curbp)));
        printf("halloc = %d, falloc = %d\n", GET_ALLOC(HDRP(curbp)), GET_ALLOC(FTRP(curbp)));
        printf("\n");
    }
    //epilogue blocks
    printf("address = %p\n", curbp);
    printf("hsize = %d\n", GET_SIZE(HDRP(curbp)));
    printf("halloc = %d\n", GET_ALLOC(HDRP(curbp)));
    printf("=========================== %s ===========================\n" ,func);
}

 mm_printblock(VERBOSE, __func__);
 
./mdriver -V -f short1-bal.rep > out.txt

#ifdef DEBUG的理解-CSDN博客

在工程设置里有一些设置会对该工程自动产生一系列的宏,用以控制程序的编译和运行。如果你把代码夹在#ifdef DEBUG 和对应的 #endif 中间,那么这段代码只有在调试(DEBUG)下才会被编译。也就是说,如果你在RELEASE模式下,这些代码根本就不会存在于你的最终代码里头。

这些宏代码本身是面向编译器使用的,不要用来实现你的业务逻辑代码,这类宏定义的一个典型应用就是产生/屏蔽调试信息。

相关推荐

  1. Lab 5: Python Lists, Trees

    2024-07-13 04:52:02       53 阅读
  2. 5G LAN

    2024-07-13 04:52:02       43 阅读
  3. CMPSC473 malloclab: writing a dynamic storage allocator

    2024-07-13 04:52:02       26 阅读
  4. CS144 Lab Checkpoint 5: down the stack (the network interface)

    2024-07-13 04:52:02       26 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-13 04:52:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-13 04:52:02       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-13 04:52:02       58 阅读
  4. Python语言-面向对象

    2024-07-13 04:52:02       69 阅读

热门阅读

  1. 小试epoll

    2024-07-13 04:52:02       26 阅读
  2. HTTP模块

    2024-07-13 04:52:02       23 阅读
  3. git diff,stash,submodule,format-patch

    2024-07-13 04:52:02       27 阅读
  4. linux系统安全加固

    2024-07-13 04:52:02       19 阅读
  5. ACE之ACE_Time_Value

    2024-07-13 04:52:02       23 阅读
  6. 力扣 150题 逆波兰表达式求值 记录

    2024-07-13 04:52:02       30 阅读
  7. cin和getline的区别

    2024-07-13 04:52:02       22 阅读
  8. STM32F103RC使用HAL库配置USART进行数据收发

    2024-07-13 04:52:02       27 阅读
  9. 07-7.5.3 处理冲突的方法

    2024-07-13 04:52:02       24 阅读
  10. Vue的import什么时候用大括号

    2024-07-13 04:52:02       22 阅读
  11. Spring Boot 框架知识汇总

    2024-07-13 04:52:02       25 阅读
  12. SpringBoot源码阅读(11)——后处理器2

    2024-07-13 04:52:02       23 阅读
  13. redis的发布与订阅

    2024-07-13 04:52:02       24 阅读
  14. Vue Router 4:构建高效单页面应用的路由管理

    2024-07-13 04:52:02       24 阅读
  15. c++【入门】病狗问题

    2024-07-13 04:52:02       21 阅读
  16. UE5 04-重新加载当前场景

    2024-07-13 04:52:02       25 阅读