目录
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、总结
懵懵懂懂,请多指教!