Malloc Lab 学生实现他们自己版本的 malloc,free 和 realloc,当然不仅要正确的实现相关功能,也要满足速度效率等要求
开始实验之前需要一些前置知识
动态内存分配器 动态内存分配器维护了一个进程的虚拟内存区域(被称为堆)
对于每个进程,内核维护着一个变量brk(break),它指向堆的顶部
动态内存分配器把堆视为一组 不同大小的块 的集合体,每个块就是一个连续的虚拟内存片(chunk),它有两种状态:已分配(allocated),空闲(free)
动态内存分配器有两种风格,两个风格都要求应用 显式地 分配内存,不同之处在于哪个实体来负责释放已分配的块
显式分配器:要求应用 显式地 释放已分配的块(主动释放,只有调用相应的函数才能将chunk释放掉),比如:C中的 malloc 和 free,C++中的 new 和 delete
隐式分配器:要求应用 隐式地 释放已分配的块(自动释放,要求分配器自己检测chunk何时不被程序利用),比如:java,ML,Lisp都依赖 “垃圾收集” 来释放chunk
分配器的基本规则 在写分配器前,先要了解malloc的功能的特点:
基础功能
malloc 可以在 heap 获取一片内存(由数据结构chunk进行管理),并返回“chunk->FD”
如果 malloc 遇到问题(例如,程序要求的内存块比可用的虚拟内存还要大),那么它就返回 NULL,并设置 errno
free 可以释放一片内存空间
内存对齐
malloc 会返回至少为“chunk->size”字节的的chunk,chunk的大小也会受 内存对齐 的影响
案例:32位程序,一个空格代表4字节,初始16字,双字对齐
+
+ 1:malloc申请了4字大小的chunk,实际上申请了4字
+ 2:malloc申请了5字大小的chunk,实际上申请了6字(双字对齐)
+ 3:malloc申请了6字大小的chunk,实际上申请了6字
+ 4:free释放了P2
+ 5:malloc申请了2字大小的chunk,优先占用了P2的 free chunk(fastbin & Tcachebin)
**内存管理**
内存管理可以分为以下几个问题:
+ 空闲块的组织方式
+ 空闲块的再申请
+ 空闲块的分割
+ 空闲块的合并
为了应对这些问题,我们需要学习相应的技术
**必要检查**
先抛开安全方面的检查,malloc必须要满足一些约束条件:
+ 每个释放请求必须对应一个当前已分配的块,这个块是由一个以前的分配请求获得到的
+ 立刻响应请求
+ 不修改已经分配的chunk(申请的chunk不重叠)
## 内存块组织技术
针对空闲块的组织方法有以下三种:
+ 隐式空闲链表(implicit free list)
+ 显式空闲链表(explicit free list)
+ 分离空闲链表(segregated free list)
这里主要介绍前两种
**隐式空间链表**
任何实际的分配器都需要一些数据结构,允许它来区别块边界,以及区别已分配块和空闲块,大多数分配器将这些信息嵌入块本身:
隐式空闲链表就是一种简单空闲块组织结构,可以把堆 按地址顺序 组织成一个连续的,包含allocated chunk和free chunk的链表
阴影部分是已分配块,没有阴影的部分是空闲块,头部标记为(size / flag)
因为分配器需要通过 flag 才能辨别该chunk的类型,所以它在重新申请chunk时,需要遍历整个链表,以获取合适的free chunk
显式空闲链表
块分配与堆块的总数呈线性关系,所以对于通用的分配器,隐式空闲链表是不适合的,一种更好的方法是 将空闲块组织为某种形式 的显式数据结构
通过“free chunk->FD”和“free chunk->BK”把free chunk连接为一个双向链表
此后进行遍历时,就不用对allocated chunk进行操作了
放置策略 为了提高内存利用率,空闲块的再申请是必不可少的,想要获取合适的空闲块,程序必须先进行搜索,分配器执行这种搜索的方式是由放置策略(placement policy)确定的,一些常见的策略是:
首次适配(first-fit)
下次适配(next-fit)
最佳适配(best-fit)
比如:隐式空闲链表中的最佳适配,就是采用遍历所有chunk方式
首次适配
首次适配算法的分配内存的设计思路是:物理内存页管理器顺着双向链表进行搜索空闲内存区域,直到找到一个足够大的空闲区域,然后立刻将其进行分配(或分割后分配),此后的内存块都不做处理
优点:速度迅速
缺点:分割后剩下的内存块会越来越难以利用
下次适配
下次适配和首次适配相似,只不过不是每次都从链表的起始处开始搜索,而是从上一次查询结束的地方开始
最佳适配
每一次都遍历所有内存块,获取最合适的
优点:剩下的内存块利用率较高
缺点:速度缓慢
空闲块分割 在 “空闲块再申请” 的过程中,不一定每一次都可以刚好分离相同大小的空闲块,所以在必要时刻需要对空闲块进行分割
对其再申请4字后,剩下的4字大小的free chunk更加难以利用了
边界标记 边界标记(boundary tag),允许在常数时间内进行对前面块的合并
在每个块的结尾处添加一个脚部 (footer,边界标记)
// 原本内存块的 头部 就可以标记chunk的状态,脚部只是头部的一个副本
如果每个块包括这样一个脚部,那么分配器就可以通过检査它的脚部,判断前面一个块的起始位置和状态,这个脚部总是在距当前块开始位置一个字的距离
考虑当分配器释放当前块时所有可能存在的情况:
情况1:前面的块和后面的块都是已分配的
情况2:前面的块是已分配的,后面的块是空闲的
情况3:前面的块是空闲的,而后面的块是已分配的
情况4:前面的和后面的块都是空闲的
有3个独立的内存块,释放中间那个内存块:
情况1:不进行合并
情况2:通过更新脚部的位置,和后面的块进行合并
情况3:通过更新头部的位置,和前面的块进行合并
情况4:同时更新头部和脚部的位置,和相邻两个块进行合并
缺陷:它要求每个块都保持一个头部和一个脚部,在应用程序操作许多个小块时,会产生显著的内存开销
性能提升 当所有malloc的基础要求完成过后,我们就需要考虑它的性能,分配器的编写者往往需要实现 吞吐率 最大化,和 内存使用率最大化 ,但是这两个性能又往往是冲突的
吞吐率最大化
吞吐率:单位时间里完成的请求数,比如:1秒完成500次申请请求,500次释放请求,那么它的吞吐率就是每秒1000次操作
合理性能:分配请求的 最糟运行时间 与 空闲块数量 形成的线性关系
内存利用率最大化
一个系统内所有进程分配的虚拟内存大全部数量,受磁盘上交换空间的数量限制
峰值利用率:用来描述分配器使用堆的效率
实验程序介绍 实验给出了“memlib.c”,提供了一个内存系统模拟程序,可以在不干涉系统内存的前提下进行实验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <sys/mman.h> #include <string.h> #include <errno.h> #include "memlib.h" #include "config.h" static char *mem_start_brk; static char *mem_brk; static char *mem_max_addr; void mem_init (void ) { if ((mem_start_brk = (char *)malloc (MAX_HEAP)) == NULL ) { fprintf (stderr , "mem_init_vm: malloc error\n" ); exit (1 ); } mem_max_addr = mem_start_brk + MAX_HEAP; mem_brk = mem_start_brk; } void mem_deinit (void ) { free (mem_start_brk); } void mem_reset_brk () { mem_brk = mem_start_brk; } void *mem_sbrk (int incr) { char *old_brk = mem_brk; if ( (incr < 0 ) || ((mem_brk + incr) > mem_max_addr)) { errno = ENOMEM; fprintf (stderr , "ERROR: mem_sbrk failed. Ran out of memory...\n" ); return (void *)-1 ; } mem_brk += incr; return (void *)old_brk; } void *mem_heap_lo () { return (void *)mem_start_brk; } void *mem_heap_hi () { return (void *)(mem_brk - 1 ); } size_t mem_heapsize () { return (size_t )(mem_brk - mem_start_brk); } size_t mem_pagesize () { return (size_t )getpagesize(); }
void *mem sbrk(int incr):和系统的 sbrk 一致,用于分配内存
void *mem_heap_lo(void):返回指向堆的第一个字节的指针
void *mem_heap_hi(void):返回指向堆的最后一个字节的指针
size_t mem_heapsize(void):返回当前的堆大小
size_t mem_pagesize(void):返回系统的 page size(页大小)
分配器包含在“mm.c”,需要完成下面几个函数:
1 2 3 4 int mm_init (void ) ; void *mm_malloc (size_t size) ; void mm_free (void *ptr) ;void *mm_realloc (void *ptr, size_t size) ;
最后,实验还给出了调试程序“mdriver.c”,可以给你的作品打分
隐式空闲链表+首次适配 第一次实验采用CSAPP上的案例,后续在进行修改
写程序之前需要先写入基本宏定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #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)))
PACK(size, alloc):把”chunk->size”和”P”包装为1位
GET(p):从 p 中读取4字节的数据( 这里强制类型转换是至关重要的 )
PUT(p, val) :将 val 存放在参数 p 指向的地址中
GET_SIZE(p):获取头部的SIZE(通过GET(p)获取头部,接着“UNPACK”)
GET_ALLOC(p):获取”P位”
HDRP(bp):返回指向当前块头部的指针(减去一字刚好计算出头部)
FTRP(bp):返回指向当前块脚部的指针(加上SIZE后,相当于多加了一个脚部,所以减两字)
NEXT_BLKP(bp):返回指向后面块BP的指针(从当前块的头部获取SIZE,相加)
PREV_BLKP(bp):返回指向前面块BP的指针(从上一个块的脚部获取SIZE,相减)
注意:因为“FTRP”是根据“HDRP”计算出的,所以必须先写入头部,才能写入脚部
对堆进行初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 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 ; }
创建一个空的空闲链表(采用隐式空间链表):
heap_listp用于记录第一个内存块的数据区(与脚部重合,但是可以被宏定义识别)
第一个内存块不写入数据(没有意义,后续操作都将会跳过它,直接获取下一个内存块)
接着它调用 extend_heap 函数
这个函数用于申请内存块,初始化使将堆扩展 CHUNKSIZE 字节,并且创建初始的空闲块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static void *extend_heap (size_t words) { void *bp; size_t size; size = (words % 2 ) ? (words + 1 ) * WSIZE : words * WSIZE; if ((long )(bp = mem_sbrk(size)) == -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); }
extend_heap保证了,“mem_sbrk(size)”的size大小为8的倍数(内存对齐)
后续的PUT填入了当前块的头部和脚部,和下一个块的头部
函数coalesce用于合并堆块,在mm_free中也会使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 void mm_free (void *bp) { size_t size = GET_SIZE(HDRP(bp)); PUT(HDRP(bp), PACK(size, 0 )); PUT(FTRP(bp), PACK(size, 0 )); coalesce(bp); } 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(NEXT_BLKP(bp)), PACK(size, 0 )); } else if (!prev_alloc && next_alloc) { size += GET_SIZE(HDRP(PREV_BLKP(bp))); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0 )); PUT(FTRP(bp), PACK(size, 0 )); bp = PREV_BLKP(bp); }else { size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(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; }
coalesce先获取了上一个块的脚部,下一个块的头部,和当前的SIZE
case1:上一个块allocated,下一个块allocated,不合并
case2:上一个块allocated,下一个块free,把“SIZE”加上“NEXT_BLKP->SIZE”,更新脚部
case3:上一个块free,下一个块allocated,把“SIZE”加上“PREV_BLKP->SIZE”,更新头部
case4:上一个块free,下一个块free,把“SIZE”同时加上前后两者的“SIZE”,更新头部&脚部
mm_malloc函数会先检查请求的真假,然后进行申请,必须保证8字节对齐,另外需要8字节来存放头部和脚部,对于超过8字节的请求,一般的规则是加上开销字节,然后向上舍入到最接近的8的整数倍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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; }
mm_malloc中的:
1 asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1 )) / DSIZE);
保证了用于申请的asize一定为8的倍数,并且留有8字节头部和脚部的空间
find_fit用于查找合适的空闲块,place用于放置空闲块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static void *find_fit (size_t asize) { char *bp = heap_listp; size_t alloc; size_t size; while (GET_SIZE(HDRP(NEXT_BLKP(bp))) > 0 ) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } return NULL ; }
采用首次适配(first-fit):遇到allocated chunk,或者free chunk的size不够,就重新循环
直到找寻到合适的空闲块后,输出其BP(数据区指针)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static void place (void *bp, size_t asize) { size_t size = GET_SIZE(HDRP(bp)); if ((size - asize) >= (2 *DSIZE)) { PUT(HDRP(bp),PACK(asize,1 )); PUT(FTRP(bp),PACK(asize,1 )); PUT(HDRP(NEXT_BLKP(bp)),PACK(size - asize,0 )); PUT(FTRP(NEXT_BLKP(bp)),PACK(size - asize,0 )); } else { PUT(HDRP(bp),PACK(size,1 )); PUT(FTRP(bp),PACK(size,1 )); } }
因为内存块最小为“ 2*DSIZE ”(16字节),所以如果切割剩余内存块小于16字节就不能进行切割,利用边界标记技术,可以通过“添加内存块边界”来进行切割
更新一下mm_realloc:
1 2 3 4 5 6 7 8 9 10 11 12 void *mm_realloc (void *ptr, size_t size) { size_t oldsize = GET_SIZE(HDRP(ptr)); void * newptr = mm_malloc(size); if (!newptr) return 0 ; if (!ptr) return newptr; memcpy (newptr, ptr, (size < oldsize ? size : oldsize)); mm_free(ptr); return newptr; }
整体逻辑:
先用“extend_heap”初始化一个大小为CHUNKSIZE的大内存块,使其为free状态
后续的malloc都在这个大内存块中分割
如果大内存块不够用,就再次调用“extend_heap”进行申请
打分&完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ➜ [/home/ywhkkx/malloclab-handout] ./mdriver -v Team Name:ateam Member 1 :Harry Bovik:bovik@cs.cmu.edu Using default tracefiles in /home/ywhkkx/malloclab-handout/traces/ Measuring performance with gettimeofday () . Results for mm malloc : trace valid util ops secs Kops 0 yes 99% 5694 0.006554 869 1 yes 99% 5848 0.005849 1000 2 yes 99% 6648 0.010224 650 3 yes 100% 5380 0.006037 891 4 yes 66% 14400 0.000076188976 5 yes 92% 4800 0.005052 950 6 yes 92% 4800 0.006428 747 7 yes 55% 12000 0.070162 171 8 yes 51% 24000 0.237273 101 9 yes 27% 14401 0.047580 303 10 yes 34% 14401 0.001504 9575 Total 74% 112372 0.396739 283 Perf index = 44 (util) + 19 (thru) = 63 /100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <string.h> #include "mm.h" #include "memlib.h" #define ALIGNMENT 8 #define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7) #define SIZE_T_SIZE (ALIGN(sizeof(size_t))) #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))) static void * extend_heap (size_t words) ;static void * coalesce (void * bp) ;static void *find_fit (size_t size) ;static void place (void *bp,size_t asize) ;static char *heap_listp = 0 ;int mm_init (void ) { if ((heap_listp = mem_sbrk(4 * WSIZE)) == (void *)-1 ) return -1 ; 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 ; } 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)) == -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); } 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(FTRP(NEXT_BLKP(bp)), PACK(size, 0 )); PUT(HDRP(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; } void *mm_malloc (size_t size) { char *bp; size_t asize; size_t extendsize; 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; } 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); return ; } static void *find_fit (size_t asize) { char *bp = heap_listp; size_t alloc; size_t size; while (GET_SIZE(HDRP(NEXT_BLKP(bp))) > 0 ) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } return NULL ; } static void place (void *bp, size_t asize) { size_t size = GET_SIZE(HDRP(bp)); if ((size - asize) >= (2 *DSIZE)) { PUT(HDRP(bp),PACK(asize,1 )); PUT(FTRP(bp),PACK(asize,1 )); PUT(HDRP(NEXT_BLKP(bp)),PACK(size - asize,0 )); PUT(FTRP(NEXT_BLKP(bp)),PACK(size - asize,0 )); } else { PUT(HDRP(bp),PACK(size,1 )); PUT(FTRP(bp),PACK(size,1 )); } } void *mm_realloc (void *ptr, size_t size) { size_t oldsize = GET_SIZE(HDRP(ptr)); void * newptr = mm_malloc(size); if (!newptr) return 0 ; if (!ptr) return newptr; memcpy (newptr, ptr, (size < oldsize ? size : oldsize)); mm_free(ptr); return newptr; }
隐式空闲链表+下次适配 想要实现下次适配的代码和书上有所不同,需要有所改进
首先需要一个全局指针来记录“上一次操作”
在一些函数快结束时(不包括“place”和“coalesce”),该指针都要更新为当前块的BP(指向数据区的指针)
另外,“find_fit”需要重写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static void *find_fit (size_t asize) { char *bp = pre_listp; size_t alloc; size_t size; while (GET_SIZE(HDRP(NEXT_BLKP(bp))) > 0 ) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } bp = heap_listp; while (bp != pre_listp) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } return NULL ; }
这里需要注意:
当下次适配遍历完所有的空闲块时,需要把BP重置为heap_listp(第一个内存块)
只需要“mm_malloc”中记录一下“pre_listp”,重复记录可能会报错
可以把“pre_listp”初始化为第二个内存块的BP(比如本程序),也可以初始化为第一个
打分&完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ➜ [/home/ywhkkx/malloclab-handout] ./mdriver -v Team Name:ateam Member 1 :Harry Bovik:bovik@cs.cmu.edu Using default tracefiles in /home/ywhkkx/malloclab-handout/traces/ Measuring performance with gettimeofday () . Results for mm malloc : trace valid util ops secs Kops 0 yes 89% 5694 0.001789 3183 1 yes 91% 5848 0.001089 5371 2 yes 95% 6648 0.003659 1817 3 yes 97% 5380 0.002975 1808 4 yes 66% 14400 0.000112128228 5 yes 92% 4800 0.003465 1385 6 yes 90% 4800 0.002659 1805 7 yes 55% 12000 0.006609 1816 8 yes 51% 24000 0.006411 3744 9 yes 27% 14401 0.041252 349 10 yes 30% 14401 0.001649 8734 Total 71% 112372 0.071668 1568 Perf index = 43 (util) + 40 (thru) = 83 /100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <string.h> #include "mm.h" #include "memlib.h" #define ALIGNMENT 8 #define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7) #define SIZE_T_SIZE (ALIGN(sizeof(size_t))) #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))) 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 char * heap_listp;static char * pre_listp;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); pre_listp = (unsigned int )heap_listp + DSIZE; if (extend_heap(CHUNKSIZE / WSIZE) == NULL ) return -1 ; return 0 ; } 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)) == -1 ) return NULL ; PUT(HDRP(bp), PACK(size, 0 )); PUT(FTRP(bp), PACK(size, 0 )); PUT(HDRP(NEXT_BLKP(bp)), PACK(0 , 1 )); pre_listp = coalesce(bp); return pre_listp; } 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; if (prev_alloc && !next_alloc) { size += GET_SIZE(HDRP(NEXT_BLKP(bp))); PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0 )); PUT(HDRP(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(FTRP(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; } 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); pre_listp = bp; return bp; } extendsize = MAX(asize, CHUNKSIZE); if ((bp = extend_heap(extendsize / WSIZE)) == NULL ) return NULL ; place(bp, asize); pre_listp = bp; return bp; } void mm_free (void * bp) { size_t size = GET_SIZE(HDRP(bp)); PUT(HDRP(bp), PACK(size, 0 )); PUT(FTRP(bp), PACK(size, 0 )); pre_listp = coalesce(bp); return ; } static void * find_fit (size_t asize) { char * bp = pre_listp; size_t alloc; size_t size; while (GET_SIZE(HDRP(NEXT_BLKP(bp))) > 0 ) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } bp = heap_listp; while (bp != pre_listp) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } return NULL ; } static void place (void * bp, size_t asize) { size_t size = GET_SIZE(HDRP(bp)); if ((size - asize) >= (2 * DSIZE)) { PUT(HDRP(bp), PACK(asize, 1 )); PUT(FTRP(bp), PACK(asize, 1 )); PUT(HDRP(NEXT_BLKP(bp)), PACK(size - asize, 0 )); PUT(FTRP(NEXT_BLKP(bp)), PACK(size - asize, 0 )); } else { PUT(HDRP(bp), PACK(size, 1 )); PUT(FTRP(bp), PACK(size, 1 )); } return ; } void * mm_realloc (void * ptr, size_t size) { size_t oldsize = GET_SIZE(HDRP(ptr)); void * newptr = mm_malloc(size); if (!newptr) return 0 ; pre_listp = newptr; if (!ptr) return newptr; memcpy (newptr, ptr, (size < oldsize ? size : oldsize)); mm_free(ptr); return newptr; }
显式空间链表+首次适配+LIFO队列 上两次试验采用了隐式空间链表,这次尝试一些显示空间链表
显示空间链表需要把空闲块链接到一起,所以需要一个宏定义来指向下一个空闲块
1 #define NEXT_RP(bp) ((unsigned int)(bp)+WSIZE)
显示空间链表采用双向链表,至少需要16字节,在空闲块中:
BP 指向(前驱):上一个空闲块
BP+4 指向(后继):下一个空闲块
另外还需要两个含函数来进行“插入”和“脱链”操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 inline void __insert(char *bp){ char *next = GET(root); if (next != NULL ) PUT(next, bp); PUT(NEXT_RP(bp), next); PUT(root,bp); } inline void __remove(char *bp){ char *pre = GET((bp)); char *next = GET(NEXT_RP(bp)); if (pre == NULL ){ if (next != NULL ) PUT(next,0 ); PUT(root,next); } else { if (next != NULL ) PUT((next),pre); PUT(NEXT_RP(pre),next); } PUT(NEXT_RP(bp),0 ); PUT(bp,0 ); }
在显式空间链表采用专门的链表来管理空闲块,这里采用后入先出队列
插入的逻辑:把root当成第一个空闲块,以后的空闲块都插入root的前驱(先写旧空闲块的后驱,再写新内存块的前驱)
脱链的逻辑:分为4种情况
前有后有:中间某个 chunk,修改 nextchunk->last 为 lastchunk,修改 lastchunk->next 为 nextchunk,置空 chunk
前有后无:root 的最后一个chunk,置空 lastchunk->next,置空 chunk
前无后有:root 的第一个chunk,置空 nextchunk->last,修改 root 为 nextchunk,置空 chunk
前无后无:root 的第一个并且是唯一一个chunk,直接置空 root,置空 chunk
下面是root依次插入 A,B,C,D 时的反应:(经典插头)
1 2 3 4 5 root(NULL ) root(A) => A(/NULL ) root(B) => B(/A) => A(B/NULL ) root(C) => C(/B) => B(C/A) => A(B/NULL ) root(D) => D(/C) => C(D/B) => B(C/A) => A(B/NULL )
下面是root分别释放 A,B,C,D 时的反应:
1 2 3 4 A: root(D) => D(/C) => C(D/B) => B(C/NULL ) B: root(D) => D(/C) => C(D/A) => A(C/NULL ) C: root(D) => D(/B) => B(D/A) => A(B/NULL ) D: root(C) => C(/B) => B(C/A) => A(B/NULL )
初始化函数发生了变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int mm_init (void ) { if ((heap_listp = mem_sbrk(6 *WSIZE))==(void *)-1 ) return -1 ; PUT(heap_listp,0 ); PUT(heap_listp+(1 *WSIZE),0 ); PUT(heap_listp+(2 *WSIZE),0 ); PUT(heap_listp+(3 *WSIZE),PACK(DSIZE,1 )); PUT(heap_listp+(4 *WSIZE),PACK(DSIZE,1 )); PUT(heap_listp+(5 *WSIZE),PACK(0 ,1 )); root = heap_listp + (1 *WSIZE); heap_listp += (4 *WSIZE); if ((extend_heap(CHUNKSIZE/DSIZE)) == NULL ) return -1 ; return 0 ; }
根节点就是第一次内存块的前驱(在root用完之前都是不变的)
合并模块对空闲块的管理更为复杂了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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){ size += GET_SIZE(HDRP(NEXT_BLKP(bp))); __remove(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))); __remove(PREV_BLKP(bp)); PUT(FTRP(bp),PACK(size,0 )); PUT(HDRP(PREV_BLKP(bp)),PACK(size,0 )); bp = PREV_BLKP(bp); } else if (!prev_alloc && !next_alloc){ size +=GET_SIZE(FTRP(NEXT_BLKP(bp)))+ GET_SIZE(HDRP(PREV_BLKP(bp))); __remove(PREV_BLKP(bp)); __remove(NEXT_BLKP(bp)); PUT(FTRP(NEXT_BLKP(bp)),PACK(size,0 )); PUT(HDRP(PREV_BLKP(bp)),PACK(size,0 )); bp = PREV_BLKP(bp); } __insert(bp); return bp; }
只要空闲块发生过合并,就会被“remove”移出链表,在最后“insert”插入链表最前端
现在查找模块只需要在空闲块链表中查询就行了:(从头开始寻找)
1 2 3 4 5 6 7 8 9 10 static void *find_fit (size_t size) { char *bp = GET(root); while (bp != NULL ){ if (GET_SIZE(HDRP(bp)) >= size) return bp; bp = GET(NEXT_RP(bp)); } return NULL ; }
先获取链表最前端的空闲块,依次向后索引
放置模块需要多考虑前驱和后继的初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static void place (void *bp,size_t asize) { size_t csize = GET_SIZE(HDRP(bp)); __remove(bp); if (csize-asize >= 2 *DSIZE){ PUT(HDRP(bp),PACK(asize,1 )); PUT(FTRP(bp),PACK(asize,1 )); PUT(HDRP(NEXT_BLKP(bp)),PACK(csize-asize,0 )); PUT(FTRP(NEXT_BLKP(bp)),PACK(csize-asize,0 )); PUT(NEXT_RP(bp),0 ); PUT((NEXT_BLKP(bp)),0 ); coalesce(NEXT_BLKP(bp)); } else { PUT(HDRP(bp),PACK(csize,1 )); PUT(FTRP(bp),PACK(csize,1 )); } }
其他模块和书上的代码差不多,需要注意一下:何时该“插入”,何时该“脱链”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ➜ [/home/ywhkkx/malloclab-handout] ./mdriver -v Team Name:ateam Member 1 :Harry Bovik:bovik@cs.cmu.edu Using default tracefiles in /home/ywhkkx/malloclab-handout/traces/ Measuring performance with gettimeofday () . Results for mm malloc : trace valid util ops secs Kops 0 yes 89% 5694 0.000167 34157 1 yes 92% 5848 0.000093 62747 2 yes 94% 6648 0.000327 20355 3 yes 96% 5380 0.000195 27661 4 yes 99% 14400 0.000093155005 5 yes 88% 4800 0.000390 12301 6 yes 85% 4800 0.000413 11625 7 yes 55% 12000 0.001840 6522 8 yes 51% 24000 0.001684 14249 9 yes 26% 14401 0.041531 347 10 yes 34% 14401 0.001982 7266 Total 74% 112372 0.048714 2307 Perf index = 44 (util) + 40 (thru) = 84 /100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <string.h> #include "mm.h" #include "memlib.h" #define ALIGNMENT 8 #define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7) #define SIZE_T_SIZE (ALIGN(sizeof(size_t))) #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))) #define NEXT_RP(bp) ((unsigned int)(bp)+WSIZE) int mm_check (char * function) ;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) ;void __insert(char * p);void __remove(char * p);static char * heap_listp = NULL ;static char * root = NULL ;int mm_init (void ) { if ((heap_listp = mem_sbrk(6 * WSIZE)) == (void *)-1 ) return -1 ; PUT(heap_listp, 0 ); PUT(heap_listp + (1 * WSIZE), 0 ); PUT(heap_listp + (2 * WSIZE), 0 ); PUT(heap_listp + (3 * WSIZE), PACK(DSIZE, 1 )); PUT(heap_listp + (4 * WSIZE), PACK(DSIZE, 1 )); PUT(heap_listp + (5 * WSIZE), PACK(0 , 1 )); root = heap_listp + (1 * WSIZE); heap_listp += (4 * WSIZE); if ((extend_heap(CHUNKSIZE / DSIZE)) == NULL ) return -1 ; return 0 ; } static void * extend_heap (size_t words) { char * bp; size_t size; size = (words % 2 ) ? (words + 1 ) * DSIZE : words * DSIZE; if ((long )(bp = mem_sbrk(size)) == -1 ) return NULL ; PUT(HDRP(bp), PACK(size, 0 )); PUT(FTRP(bp), PACK(size, 0 )); PUT(HDRP(NEXT_BLKP(bp)), PACK(0 , 1 )); PUT(NEXT_RP(bp), 0 ); PUT(bp, 0 ); return coalesce(bp); } 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) { size += GET_SIZE(HDRP(NEXT_BLKP(bp))); __remove(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))); __remove(PREV_BLKP(bp)); PUT(FTRP(bp), PACK(size, 0 )); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0 )); bp = PREV_BLKP(bp); } else if (!prev_alloc && !next_alloc) { size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp))); __remove(PREV_BLKP(bp)); __remove(NEXT_BLKP(bp)); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0 )); PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0 )); bp = PREV_BLKP(bp); } __insert(bp); return bp; } 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 / DSIZE)) == NULL ) return NULL ; place(bp, asize); return bp; } void mm_free (void * bp) { if (bp == NULL ) return ; size_t size = GET_SIZE(HDRP(bp)); PUT(HDRP(bp), PACK(size, 0 )); PUT(FTRP(bp), PACK(size, 0 )); PUT(NEXT_RP(bp), 0 ); PUT(bp, 0 ); coalesce(bp); } static void * find_fit (size_t size) { char * bp = GET(root); while (bp != NULL ) { if (GET_SIZE(HDRP(bp)) >= size) return bp; bp = GET(NEXT_RP(bp)); } return NULL ; } static void place (void * bp, size_t asize) { size_t size = GET_SIZE(HDRP(bp)); __remove(bp); if ((size - asize) >= (2 * DSIZE)) { PUT(HDRP(bp), PACK(asize, 1 )); PUT(FTRP(bp), PACK(asize, 1 )); PUT(HDRP(NEXT_BLKP(bp)), PACK(size - asize, 0 )); PUT(FTRP(NEXT_BLKP(bp)), PACK(size - asize, 0 )); PUT(NEXT_RP(bp), 0 ); PUT((NEXT_BLKP(bp)), 0 ); coalesce(NEXT_BLKP(bp)); } else { PUT(HDRP(bp), PACK(size, 1 )); PUT(FTRP(bp), PACK(size, 1 )); } } void * mm_realloc (void * ptr, size_t size) { void * newptr = mm_malloc(size); if (!newptr) { return 0 ; } if (ptr == NULL ) { return newptr; } size_t oldsize = GET_SIZE(HDRP(ptr)); memcpy (newptr, ptr, oldsize < size ? oldsize : size); mm_free(ptr); return newptr; } inline void __insert(char * bp){ char * next = GET(root); if (next != NULL ) PUT(next, bp); PUT(NEXT_RP(bp), next); PUT(root, bp); } inline void __remove(char * bp){ char * pre = GET((bp)); char * next = GET(NEXT_RP(bp)); if (pre == NULL ) { if (next != NULL ) PUT(next, 0 ); PUT(root, next); } else { if (next != NULL ) PUT((next), pre); PUT(NEXT_RP(pre), next); } PUT(NEXT_RP(bp), 0 ); PUT(bp, 0 ); }
隐式空闲链表+下次适配+增添标记位 和第二次试验很相似,只不过需要去除ALLOC内存块的脚部,并在一些位置进行修改
开始实验之前先进行一下思考:假设ALLOC内存块没有脚部
1 #define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
那么,宏定义“PREV_BLKP(bp)”将会失效,ALLOC内存块将不能获取它前面的内存块,回看第二次实验的代码,发现就只有合并模块会受到影响:刚刚被free的ALLOC内存块不清楚上一个内存块是否为ALLOC
解决办法:对标记位进行操作,使头部可以表示上一个内存块的状态
分配的内存块大小为8的倍数,最后3位数值恒定,理论上有3个标志位可以用,以上代码中,最后一个标志位被用于记录“当前内存块是否为ALLOC”,与之类似,我们可以使用倒数第二个标志位来记录“上一个内存块是否为ALLOC”
1 2 3 #define PREALLOC(x) ((!x) ? 0 : 2) #define PACK(size, prealloc, alloc) ((size) | (PREALLOC(prealloc)) | (alloc)) #define GET_PREALLOC(p) (GET(p) & 0x2)
这些宏定义可以简化我们的操作
1 2 3 4 5 6 inline void set_next_prealloc (void * bp, size_t prealloc) { size_t size = GET_SIZE(HDRP(NEXT_BLKP(bp))); size_t alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); PUT(HDRP(NEXT_BLKP(bp)), PACK(size,prealloc,alloc)); }
这个函数可以根据当前内存块的状态,来更新下一个内存块的头部
接下来的代码和第二次实验区别不大,但需要注意一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void * mm_malloc (size_t size) { size_t asize; size_t extendsize; char * bp; if (size <= 0 ) return NULL ; if (size <= WSIZE) asize = DSIZE; else asize = DSIZE * ((size + WSIZE + (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; }
“mm_malloc”获取ALLOC内存块时,不用写入脚部,可以少申请4字节,最小字节数可以变为“8”
打分&完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ➜ [/home/ywhkkx/malloclab-handout] ./mdriver -v Team Name:ateam Member 1 :Harry Bovik:bovik@cs.cmu.edu Using default tracefiles in /home/ywhkkx/malloclab-handout/traces/ Measuring performance with gettimeofday () . Results for mm malloc : trace valid util ops secs Kops 0 yes 89% 5694 0.002184 2607 1 yes 92% 5848 0.001025 5704 2 yes 95% 6648 0.003881 1713 3 yes 97% 5380 0.002774 1939 4 yes 66% 14400 0.000101142292 5 yes 92% 4800 0.004425 1085 6 yes 91% 4800 0.003771 1273 7 yes 55% 12000 0.007433 1614 8 yes 51% 24000 0.007050 3404 9 yes 27% 14401 0.042339 340 10 yes 30% 14401 0.001879 7663 Total 71% 112372 0.076863 1462 Perf index = 43 (util) + 40 (thru) = 83 /100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <string.h> #include "mm.h" #include "memlib.h" #define ALIGNMENT 8 #define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7) #define SIZE_T_SIZE (ALIGN(sizeof(size_t))) #define WSIZE 4 #define DSIZE 8 #define CHUNKSIZE (1<<12) #define MAX(x,y) ((x) > (y)? (x) : (y)) #define PREALLOC(x) ((!x) ? 0 : 2) #define PACK(size,prealloc,alloc) ((size) | (PREALLOC(prealloc)) | (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 GET_PREALLOC(p) (GET(p) & 0x2) #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 * coalesce (void * bp) ;static void * find_fit (size_t asize) ;static void place (void * bp, size_t asize) ;inline void set_next_prealloc (void * bp, size_t prealloc) ;static char * heap_listp;static char * pre_listp;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 , 1 )); PUT(heap_listp + (2 * WSIZE), PACK(DSIZE, 1 , 1 )); PUT(heap_listp + (3 * WSIZE), PACK(0 , 1 , 1 )); heap_listp += (2 * WSIZE); pre_listp = heap_listp; if (extend_heap(CHUNKSIZE / WSIZE) == NULL ) return -1 ; return 0 ; } static void * extend_heap (size_t words) { char * bp; size_t size, prealloc; size = (words % 2 ) ? (words + 1 ) * WSIZE : words * WSIZE; if ((long )(bp = mem_sbrk(size)) == -1 ) return NULL ; prealloc = GET_PREALLOC(HDRP(bp)); PUT(HDRP(bp), PACK(size, prealloc, 0 )); PUT(FTRP(bp), PACK(size, prealloc, 0 )); PUT(HDRP(NEXT_BLKP(bp)), PACK(0 , 0 , 1 )); return coalesce(bp); } static void * coalesce (void * bp) { size_t prev_alloc = GET_PREALLOC(HDRP((bp))); size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); size_t size = GET_SIZE(HDRP(bp)); if (prev_alloc && next_alloc) { set_next_prealloc(bp, 0 ); pre_listp = bp; return bp; } if (prev_alloc && !next_alloc) { size += GET_SIZE(HDRP(NEXT_BLKP(bp))); PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 1 , 0 )); PUT(HDRP(bp), PACK(size, 1 , 0 )); } else if (!prev_alloc && next_alloc) { size += GET_SIZE(HDRP(PREV_BLKP(bp))); PUT(FTRP(bp), PACK(size, 1 , 0 )); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 1 , 0 )); bp = PREV_BLKP(bp); } else { size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp))); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 1 , 0 )); PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 1 , 0 )); bp = PREV_BLKP(bp); } set_next_prealloc(bp, 0 ); pre_listp = bp; return bp; } void * mm_malloc (size_t size) { size_t asize; size_t extendsize; char * bp; if (size <= 0 ) return NULL ; if (size <= WSIZE) asize = DSIZE; else asize = DSIZE * ((size + WSIZE + (DSIZE - 1 )) / DSIZE); if ((bp = find_fit(asize)) != NULL ) { place(bp, asize); pre_listp = bp; return bp; } extendsize = MAX(asize, CHUNKSIZE); if ((bp = extend_heap(extendsize / WSIZE)) == NULL ) return NULL ; place(bp, asize); pre_listp = bp; return bp; } void mm_free (void * bp) { size_t size = GET_SIZE(HDRP(bp)); size_t prealloc = GET_PREALLOC(HDRP(bp)); PUT(HDRP(bp), PACK(size, prealloc, 0 )); PUT(FTRP(bp), PACK(size, prealloc, 0 )); coalesce(bp); return ; } static void * find_fit (size_t asize) { char * bp = pre_listp; size_t alloc; size_t size; while (GET_SIZE(HDRP(NEXT_BLKP(bp))) > 0 ) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } bp = heap_listp; while (bp != pre_listp) { bp = NEXT_BLKP(bp); alloc = GET_ALLOC(HDRP(bp)); if (alloc) continue ; size = GET_SIZE(HDRP(bp)); if (size < asize) continue ; return bp; } return NULL ; } static void place (void * bp, size_t asize) { size_t size = GET_SIZE(HDRP(bp)); if ((size - asize) >= DSIZE) { PUT(HDRP(bp), PACK(asize, 1 , 1 )); PUT(FTRP(bp), PACK(asize, 1 , 1 )); PUT(HDRP(NEXT_BLKP(bp)), PACK(size - asize, 1 , 0 )); PUT(FTRP(NEXT_BLKP(bp)), PACK(size - asize, 1 , 0 )); set_next_prealloc(bp, 0 ); } else { PUT(HDRP(bp), PACK(size, 1 , 1 )); set_next_prealloc(bp, 1 ); } pre_listp = bp; } void * mm_realloc (void * ptr, size_t size) { size_t oldsize = GET_SIZE(HDRP(ptr)); void * newptr = mm_malloc(size); if (!newptr) return 0 ; pre_listp = newptr; if (!ptr) return newptr; memcpy (newptr, ptr, (size < oldsize ? size : oldsize)); mm_free(ptr); return newptr; } inline void set_next_prealloc (void * bp, size_t prealloc) { size_t size = GET_SIZE(HDRP(NEXT_BLKP(bp))); size_t alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); PUT(HDRP(NEXT_BLKP(bp)), PACK(size, prealloc, alloc)); }
分离式空闲链表+首次适配 分离式空闲链表(Segregated Free Lists),采用了 分离存储 技术,同时维护多个空闲链表,其中每个链表中的块有大致相等的大小
实现 分离存储 有许多中方法,CSAPP简单提及了三种:
这里采用 简单分离存储 的方式实现分离式空闲链表,同一区域的空闲块用前驱和后继指针进行连接
多了两个宏定义:
1 2 #define PREV_LINKNODE_RP(bp) ((char*)(bp)) #define NEXT_LINKNODE_RP(bp) ((char*)(bp)+WSIZE)
PREV_LINKNODE_RP(bp):获取上一个空闲块
NEXT_LINKNODE_RP(bp):获取下一个空闲块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int mm_init (void ) { if ((heap_listp = mem_sbrk(14 *WSIZE))==(void *)-1 ) return -1 ; PUT(heap_listp,0 ); PUT(heap_listp+(1 *WSIZE),0 ); PUT(heap_listp+(2 *WSIZE),0 ); PUT(heap_listp+(3 *WSIZE),0 ); PUT(heap_listp+(4 *WSIZE),0 ); PUT(heap_listp+(5 *WSIZE),0 ); PUT(heap_listp+(6 *WSIZE),0 ); PUT(heap_listp+(7 *WSIZE),0 ); PUT(heap_listp+(8 *WSIZE),0 ); PUT(heap_listp+(9 *WSIZE),0 ); PUT(heap_listp+(10 *WSIZE),0 ); PUT(heap_listp+(11 *WSIZE),PACK(DSIZE,1 )); PUT(heap_listp+(12 *WSIZE),PACK(DSIZE,1 )); PUT(heap_listp+(13 *WSIZE),PACK(0 ,1 )); block_list_start = heap_listp; heap_listp += (12 *WSIZE); if ((extend_heap(CHUNKSIZE/DSIZE))==NULL ) return -1 ; return 0 ; }
在初始的第一个内存块的前面:定义了若干个分离的空间链表
“extend_heap”,“mm_malloc”和第三次实验(显示空间链表)的差别不大
“find_fit”和“place”需要重写(首次适配):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 static void *find_fit (size_t size) { char *root = find_list_root(size); for (root; root!=(heap_listp-(2 *WSIZE)); root+=WSIZE) { char *tmpP = GET(root); while (tmpP != NULL ) { if (GET_SIZE(HDRP(tmpP))>=size) return tmpP; tmpP = GET(NEXT_LINKNODE_RP(tmpP)); } } return NULL ; } static void place (void *bp,size_t asize) { size_t csize = GET_SIZE(HDRP(bp)); fix_linklist(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 )); PUT(NEXT_LINKNODE_RP(bp),0 ); PUT(PREV_LINKNODE_RP(bp),0 ); coalesce(bp); } else { PUT(HDRP(bp),PACK(csize,1 )); PUT(FTRP(bp),PACK(csize,1 )); } }
Find_fit 的逻辑:首先根据size获取相应的空间链表,遍历当前链表所以的空闲块,如果没有合适的空闲块就“root+=WSIZE”,在有更大空闲块的空间链表中寻找,获取合适的空间链表后,先获取该链表中最前面的那个空闲块,再从前往后进行遍历
Place的逻辑:在判断是否切割前先脱链,接着完成后续操作
这里引入了两个对空间链表进行操作的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 inline char *find_list_root (size_t size) { int i = 0 ; if (size<=8 ) i=0 ; else if (size<=16 ) i= 1 ; else if (size<=32 ) i= 2 ; else if (size<=64 ) i= 3 ; else if (size<=128 ) i= 4 ; else if (size<=256 ) i= 5 ; else if (size<=512 ) i= 6 ; else if (size<=2048 ) i= 7 ; else if (size<=4096 ) i= 8 ; else i= 9 ; return block_list_start+(i*WSIZE); }
根据“size”获取对应空间链表的位置,不多说
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 inline void fix_linklist (char *p) { char *root = find_list_root(GET_SIZE(HDRP(p))); char *prevp = GET(PREV_LINKNODE_RP(p)); char *nextp = GET(NEXT_LINKNODE_RP(p)); if (prevp == NULL ) { if (nextp != NULL )PUT(PREV_LINKNODE_RP(nextp),0 ); PUT(root,nextp); } else { if (nextp != NULL )PUT(PREV_LINKNODE_RP(nextp),prevp); PUT(NEXT_LINKNODE_RP(prevp),nextp); } PUT(NEXT_LINKNODE_RP(p),NULL ); PUT(PREV_LINKNODE_RP(p),NULL ); }
脱链操作,跟第三次实验中的“__remove”功能类似
“mm_free”的变化不大,主要是“coalesce”变化较大:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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) { insert_to_list(bp); return bp; } if (prev_alloc && !next_alloc) { size += GET_SIZE(HDRP(NEXT_BLKP(bp))); fix_linklist(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))); fix_linklist(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))); fix_linklist(PREV_BLKP(bp)); fix_linklist(NEXT_BLKP(bp)); PUT(FTRP(NEXT_BLKP(bp)),PACK(size,0 )); PUT(HDRP(PREV_BLKP(bp)),PACK(size,0 )); bp = PREV_BLKP(bp); } insert_to_list(bp); return bp; }
在合并之后,需要把被合并的空闲块脱链,最后新的空闲块插入对应的空间链表中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 inline void insert_to_list (char *p) { char *root = find_list_root(GET_SIZE(HDRP(p))); char *prevp = root; char *nextp = GET(root); while (nextp!=NULL ) { if (GET_SIZE(HDRP(nextp))>=GET_SIZE(HDRP(p))) break ; prevp = nextp; nextp = GET(NEXT_LINKNODE_RP(nextp)); } if (prevp == root) { PUT(root,p); PUT(NEXT_LINKNODE_RP(p),nextp); PUT(PREV_LINKNODE_RP(p),NULL ); if (nextp!=NULL ) PUT(PREV_LINKNODE_RP(nextp),p); } else { PUT(NEXT_LINKNODE_RP(prevp),p); PUT(PREV_LINKNODE_RP(p),prevp); PUT(NEXT_LINKNODE_RP(p),nextp); if (nextp!=NULL ) PUT(PREV_LINKNODE_RP(nextp),p); } }
基本逻辑:
一,先获取当前空间链表,进行优化操作:
在同一个空间链表中,空闲块的大小也是有差异的,该优化操作会获取“root指向的那个空闲块”(第一个空闲块)比较改空闲块的大小,如果不符合条件,程序就会把“下一个空闲块”当做“root”继续进行后续操作(相当于把“root”和“第一个空闲块”向后“平移”了1个单位),这样操作保证了:小内存块在前,大内存块在后
二,后续操作要分情况进行:
插入该空间链表的头部:在“root”中写入指向它前驱的指针,在它的前驱中写入NULL,后继中写入第一个内存块的地址,最后调整第一个内存块的前驱指针
插入该空间链表的中段:在“prevp”的后继中写入它自己的指针,在它的前驱中写入“prevp”,后继中写入“nextp”,最后调整“nextp”(第一个内存块)的前驱指针
整体逻辑:
结合“find_fit”来看,本程序才用了 半个LIFO队列 (后入先出,但又不完全是)
获取空闲块时:从前往后进行遍历
插入空闲块时:尽可能插入头部
这种对内存块进行大小排序,优先获取小内存块的做法的确提高了效率(指多打了1分)
打分&完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ➜ [/home/ywhkkx/malloclab-handout] ./mdriver -v Team Name:ateam Member 1 :Harry Bovik:bovik@cs.cmu.edu Using default tracefiles in /home/ywhkkx/malloclab-handout/traces/ Measuring performance with gettimeofday () . Results for mm malloc : trace valid util ops secs Kops 0 yes 99% 5694 0.000104 54645 1 yes 99% 5848 0.000097 60538 2 yes 99% 6648 0.000271 24559 3 yes 100% 5380 0.000103 52182 4 yes 99% 14400 0.000096150000 5 yes 95% 4800 0.001038 4624 6 yes 95% 4800 0.000852 5636 7 yes 55% 12000 0.000177 67912 8 yes 51% 24000 0.000557 43103 9 yes 22% 14401 0.042128 342 10 yes 30% 14401 0.002138 6737 Total 77% 112372 0.047560 2363 Perf index = 46 (util) + 40 (thru) = 86 /100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <string.h> #include "mm.h" #include "memlib.h" #define ALIGNMENT 8 #define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7) #define SIZE_T_SIZE (ALIGN(sizeof(size_t))) #define WSIZE 4 #define DSIZE 8 #define CHUNKSIZE (1<<12) #define MAX(x,y) ((x) > (y)? (x) : (y)) #define PREALLOC(x) ((!x) ? 0 : 2) #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 GET_PREALLOC(p) (GET(p) & 0x2) #define HDRP(bp) ((char *)(bp) - WSIZE) #define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) #define PREV_LINKNODE_RP(bp) ((char*)(bp)) #define NEXT_LINKNODE_RP(bp) ((char*)(bp)+WSIZE) #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 * coalesce (void * bp) ;static void * find_fit (size_t asize) ;static void place (void * bp, size_t asize) ;void insert_to_list (char * p) ;void fix_linklist (char * p) ;inline char * find_list_root (size_t size) ;static char * heap_listp = NULL ;static char * block_list_start = NULL ;int mm_init (void ) { if ((heap_listp = mem_sbrk(14 * WSIZE)) == (void *)-1 ) return -1 ; PUT(heap_listp, 0 ); PUT(heap_listp + (1 * WSIZE), 0 ); PUT(heap_listp + (2 * WSIZE), 0 ); PUT(heap_listp + (3 * WSIZE), 0 ); PUT(heap_listp + (4 * WSIZE), 0 ); PUT(heap_listp + (5 * WSIZE), 0 ); PUT(heap_listp + (6 * WSIZE), 0 ); PUT(heap_listp + (7 * WSIZE), 0 ); PUT(heap_listp + (8 * WSIZE), 0 ); PUT(heap_listp + (9 * WSIZE), 0 ); PUT(heap_listp + (10 * WSIZE), 0 ); PUT(heap_listp + (11 * WSIZE), PACK(DSIZE, 1 )); PUT(heap_listp + (12 * WSIZE), PACK(DSIZE, 1 )); PUT(heap_listp + (13 * WSIZE), PACK(0 , 1 )); block_list_start = heap_listp; heap_listp += (12 * WSIZE); if ((extend_heap(CHUNKSIZE / DSIZE)) == NULL ) return -1 ; return 0 ; } static void * extend_heap (size_t dwords) { char * bp; size_t size; size = (dwords % 2 ) ? (dwords + 1 ) * DSIZE : dwords * DSIZE; if ((long )(bp = mem_sbrk(size)) == (void *)-1 ) return NULL ; PUT(HDRP(bp), PACK(size, 0 )); PUT(FTRP(bp), PACK(size, 0 )); PUT(PREV_LINKNODE_RP(bp), NULL ); PUT(NEXT_LINKNODE_RP(bp), NULL ); PUT(HDRP(NEXT_BLKP(bp)), PACK(0 , 1 )); return coalesce(bp); } 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) { insert_to_list(bp); return bp; } if (prev_alloc && !next_alloc) { size += GET_SIZE(HDRP(NEXT_BLKP(bp))); fix_linklist(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))); fix_linklist(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))); fix_linklist(PREV_BLKP(bp)); fix_linklist(NEXT_BLKP(bp)); PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0 )); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0 )); bp = PREV_BLKP(bp); } insert_to_list(bp); return bp; } 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 / DSIZE)) == NULL ) { return NULL ; } place(bp, asize); return bp; } 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 )); PUT(NEXT_LINKNODE_RP(bp), NULL ); PUT(PREV_LINKNODE_RP(bp), NULL ); coalesce(bp); } static void * find_fit (size_t size) { char * root = find_list_root(size); for (root; root != (heap_listp - (2 * WSIZE)); root += WSIZE) { char * tmpP = GET(root); while (tmpP != NULL ) { if (GET_SIZE(HDRP(tmpP)) >= size) return tmpP; tmpP = GET(NEXT_LINKNODE_RP(tmpP)); } } return NULL ; } static void place (void * bp, size_t asize) { size_t csize = GET_SIZE(HDRP(bp)); fix_linklist(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 )); PUT(NEXT_LINKNODE_RP(bp), 0 ); PUT(PREV_LINKNODE_RP(bp), 0 ); coalesce(bp); } else { PUT(HDRP(bp), PACK(csize, 1 )); PUT(FTRP(bp), PACK(csize, 1 )); } } void * mm_realloc (void * ptr, size_t size) { size_t oldsize = GET_SIZE(HDRP(ptr)); void * newptr = mm_malloc(size); if (!newptr) return 0 ; if (!ptr) return newptr; memcpy (newptr, ptr, (size < oldsize ? size : oldsize)); mm_free(ptr); return newptr; } inline char * find_list_root (size_t size) { int i = 0 ; if (size <= 8 ) i = 0 ; else if (size <= 16 ) i = 1 ; else if (size <= 32 ) i = 2 ; else if (size <= 64 ) i = 3 ; else if (size <= 128 ) i = 4 ; else if (size <= 256 ) i = 5 ; else if (size <= 512 ) i = 6 ; else if (size <= 2048 ) i = 7 ; else if (size <= 4096 ) i = 8 ; else i = 9 ; return block_list_start + (i * WSIZE); } inline void fix_linklist (char * p) { char * root = find_list_root(GET_SIZE(HDRP(p))); char * prevp = GET(PREV_LINKNODE_RP(p)); char * nextp = GET(NEXT_LINKNODE_RP(p)); if (prevp == NULL ) { if (nextp != NULL )PUT(PREV_LINKNODE_RP(nextp), 0 ); PUT(root, nextp); } else { if (nextp != NULL )PUT(PREV_LINKNODE_RP(nextp), prevp); PUT(NEXT_LINKNODE_RP(prevp), nextp); } PUT(NEXT_LINKNODE_RP(p), NULL ); PUT(PREV_LINKNODE_RP(p), NULL ); } inline void insert_to_list (char * p) { char * root = find_list_root(GET_SIZE(HDRP(p))); char * prevp = root; char * nextp = GET(root); while (nextp != NULL ) { if (GET_SIZE(HDRP(nextp)) >= GET_SIZE(HDRP(p))) break ; prevp = nextp; nextp = GET(NEXT_LINKNODE_RP(nextp)); } if (prevp == root) { PUT(root, p); PUT(NEXT_LINKNODE_RP(p), nextp); PUT(PREV_LINKNODE_RP(p), NULL ); if (nextp != NULL ) PUT(PREV_LINKNODE_RP(nextp), p); } else { PUT(NEXT_LINKNODE_RP(prevp), p); PUT(PREV_LINKNODE_RP(p), prevp); PUT(NEXT_LINKNODE_RP(p), nextp); if (nextp != NULL ) PUT(PREV_LINKNODE_RP(nextp), p); } }