本系列文章为中国科学技术大学计算机专业学科基础课《计算机系统》布置的实验,上课所用教材和内容为黑书CSAPP,当时花费很大精力和弯路,现来总结下各个实验,本文章为第五个实验——动态内存分配(Malloc lab)。
/afs/cs/project/ics/im/labs/malloclab/
在该实验中,需要用C语言实现一个动态存储分配器(dynamic storage allocater)。需要实现malloc、free、realloc等功能。当然不仅要正确的实现相关功能也要满足速度效率等要求。
tar xvf malloclab-handout.tar解压文件
我们需要修改的唯一文件是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)
mm_init:在调用mm_malloc,mm_realloc或mm_free之前,调用mm_init进行初始化,正确返回0。
mm_malloc:在堆区域分配指定大小的块,分配的空间,返回的指针应该是8字节对齐的
mm_free:释放指针指向的block
mm_realloc:返回指向一个大小为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 *mem_sbrk(int incr): Expands the heap by incr bytes, where incr is apositive non-zero integer and returns a generic pointer to the first byte of the newly allocated heap area.
void *mem_heap_lo(void):Returns a generic pointer to the first byte in the heap.
void *mem_heap_hi(void): Returns a generic pointer to the last byte in the heap.
size_t mem_heapsize(void):Returns the current size of the heap in bytes.
size_t mem_pagesize(void): Returns the system’s page size in bytes (4K onLinux systems).
mdriver.c:负责测试mm.c的正确性,空间利用率和吞吐量
-f : -f后添加一些trace file来测试我们实现的函数
-V:打印出诊断信息
./mdriver -V -f short1-bal.rep
不能改变mm.c中函数接口
不能直接调用任何内存管理的库函数和系统函数malloc, calloc, free, realloc, sbrk, brk
不能定义任何全局或者静态复合数据结构如arrays, structs, trees,允许使用integers, floats, and pointers等简单数据类型
只要提交mm.c文件
空闲块的组织方法-Segregated free list方法
segregated free list 中大小类的分类方法如下,并且将该list表放在heap的头部,通过序言块将它与数据隔离。在每一个大小类中,空闲块按照size由大到小排序。
*
* mm_init - initialize the malloc package.
* The return value should be -1 if there was a problem in performing the initialization, 0 otherwise
*/
int mm_init(void)
{
int listnumber;
char *heap;
/* 初始化分离空闲链表 */
for (listnumber = 0; listnumber < LISTMAX; listnumber++)
{
segregated_free_lists[listnumber] = NULL;
}
/* 初始化堆 */
if ((long)(heap = mem_sbrk(4 * WSIZE)) == -1)
return -1;
/* 这里的结构参见本文上面的“堆的起始和结束结构” */
PUT(heap, 0);
PUT(heap + (1 * WSIZE), PACK(DSIZE, 1));
PUT(heap + (2 * WSIZE), PACK(DSIZE, 1));
PUT(heap + (3 * WSIZE), PACK(0, 1));
/* 扩展堆 */
if (extend_heap(INITCHUNKSIZE) == NULL)
return -1;
return 0;
}
空闲块查找方法 - best fit
因为同一大小类中空闲块由小到大排序,所以查找是第一个合适的就是最适配的mm_malloc函数。
/*
* 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)
{
if (size == 0)
return NULL;
/* 内存对齐 */
if (size <= DSIZE)
{
size = 2 * DSIZE;
}
else
{
size = ALIGN(size + DSIZE);
}
int listnumber = 0;
size_t searchsize = size;
void *ptr = NULL;
while (listnumber < LISTMAX)
{
/* 寻找对应链 */
if (((searchsize <= 1) && (segregated_free_lists[listnumber] != NULL)))
{
ptr = segregated_free_lists[listnumber];
/* 在该链寻找大小合适的free块 */
while ((ptr != NULL) && ((size > GET_SIZE(HDRP(ptr)))))
{
ptr = PRED(ptr);
}
/* 找到对应的free块 */
if (ptr != NULL)
break;
}
searchsize >>= 1;
listnumber++;
}
/* 没有找到合适的free块,扩展堆 */
if (ptr == NULL)
{
if ((ptr = extend_heap(MAX(size, CHUNKSIZE))) == NULL)
return NULL;
}
/* 在free块中allocate size大小的块 */
ptr = place(ptr, size);
return ptr;
}
/*
* 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));
/* 插入分离空闲链表 */
insert_node(ptr, size);
/* 注意合并 */
coalesce(ptr);
}
mm_realloc 改进:
对于请求的newsize>原始的oldsize这种情况,我们将运用类似coalesce中的方法,先去检查前后是否有空闲块,并是否满足前后空闲块和当前已分配的空闲块size相加大于newsize,如果是则合并,不需要再重新请求空闲块。如果不行,则需要重新mm_malloc一块新的空间。
/*
* mm_realloc - Implemented simply in terms of mm_malloc and mm_free
*/
void *mm_realloc(void *ptr, size_t size)
{
void *new_block = ptr;
int remainder;
if (size == 0)
return NULL;
/* 内存对齐 */
if (size <= DSIZE)
{
size = 2 * DSIZE;
}
else
{
size = ALIGN(size + DSIZE);
}
/* 如果size小于原来块的大小,直接返回原来的块 */
if ((remainder = GET_SIZE(HDRP(ptr)) - size) >= 0)
{
return ptr;
}
/* 否则先检查地址连续下一个块是否为free块或者该块是堆的结束块,因为我们要尽可能利用相邻的free块,以此减小“external fragmentation” */
else if (!GET_ALLOC(HDRP(NEXT_BLKP(ptr))) || !GET_SIZE(HDRP(NEXT_BLKP(ptr))))
{
/* 即使加上后面连续地址上的free块空间也不够,需要扩展块 */
if ((remainder = GET_SIZE(HDRP(ptr)) + GET_SIZE(HDRP(NEXT_BLKP(ptr))) - size) < 0)
{
if (extend_heap(MAX(-remainder, CHUNKSIZE)) == NULL)
return NULL;
remainder += MAX(-remainder, CHUNKSIZE);
}
/* 删除刚刚利用的free块并设置新块的头尾 */
delete_node(NEXT_BLKP(ptr));
PUT(HDRP(ptr), PACK(size + remainder, 1));
PUT(FTRP(ptr), PACK(size + remainder, 1));
}
/* 没有可以利用的连续free块,而且size大于原来的块,这时只能申请新的不连续的free块、复制原块内容、释放原块 */
else
{
new_block = mm_malloc(size);
memcpy(new_block, ptr, GET_SIZE(HDRP(ptr)));
mm_free(ptr);
}
return new_block;
}
测试用例1:99分
测试用例2:93分