前情提要
mm.c
文件mdriver.c
可以用来衡量性能
make
./mdriver -V
int mm_init(void)
void *mm_malloc(size_t size)
void mm_free(void *ptr)
void *mm_realloc(void *ptr, size_t size)
void *mem sbrk(int incr)
void *mem heap lo(void)
:返回堆的起始位置void *mem heap hi(void)
:返回堆的终止位置size t mem heapsize(void)
返回堆的大小size t mem pagesize(void)
返回内存页面的大小int mm_init(void)
这个函数是用来初始化我们的堆,
int mm_init(void) {
// 初始化记录空闲块的数组
for (int i = 0; i < LIST_MAX_SIZE; i++) {
segregated_free_lists[i] = NULL;
}
// 先请求空间来初始化堆的结构
void *heap_listp;
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));
// 为堆申请一个chunksize的虚存空间
if (extend_heap(CHUNKSIZE) == NULL) {
return -1;
}
return 0;
}
static void *extend_heap(size_t size)
这个函数用来给堆扩展size大小的空间,要完成的功能如下所示
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)
mem_sbrk
申请size大小的空闲块static void *extend_heap(size_t size) {
// word_count必须是偶数
size = ALIGN(size);
// 使用mem_sbrk去申请空间
void *bp;
if ((bp = mem_sbrk(size)) == (void *)-1) {
return NULL;
}
// 将新申请的内存加到已有的堆上去
// 现在有个指针bp,它指向的新申请的内存的真正的起始位置
// 正常来说,这个bp的前一块就是之前堆的结尾块,是没用的,现在直接拿来做新申请的块的首部
// 然后又将新申请的块的最后一块变成堆的尾部
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));
// 将这个新的空闲块插入到我们的列表里
insert_node(bp, size);
return coalesce(bp);
}
static void insert_node(void *ptr, size_t size)
这个函数是将以ptr为bp指针的空闲块给塞到空闲块数组中去,bp指针指的是当前空闲块的有效载荷的第一个字节的地址,也就是头部+WSIZE
SET_PTR
#define SET_PTR(p, ptr) (*(unsigned int *)(p) = (unsigned int)(ptr))
*(unsigned int *)(p)
就可以得到ptr。*(unsigned int *)(p)
就可以得到pre指针或者next指针GET_PRE_PTR
#define GET_PRE_PTR(ptr) ((void *)(ptr))
GET_SUCC_PTR
的使用统一起来#define GET_SUCC_PTR(ptr) ((void *)(ptr) + WSIZE)
就获得了指向next指针的指针GET_PRE
#define GET_PRE(ptr) (*(void **)(GET_PRE_PTR(ptr)))
GET_PRE_PTR
实际上是指向pre指针的指针,但是呢,因为我们前面的定义中返回的void*
,因此,我们需要先将它强制转为void**
类型,即指向指针的指针。然后再通过*
操作取出来pre空闲块真正的地址。#define GET_SUCC(ptr) (*(void **)(GET_SUCC_PTR(ptr)))
就是取得真正的succ指针static void insert_node(void *ptr, size_t size) {
// 首先找到这个size应该在哪个格子里
int pos = find_pos(size);
// 扫描这个格子里存的链表,找到应该插入的位置
void *pre_ptr = NULL;
void *cur_ptr = segregated_free_lists[pos];
while (cur_ptr != NULL && size > GET_SIZE(HDRP(cur_ptr))) {
pre_ptr = cur_ptr;
cur_ptr = GET_SUCC(cur_ptr);
}
// 分情况讨论,正常来说,我们应该插入pre_ptr和cur_ptr之间
// 前驱比自己小,后续比自己大
// 如果pre_ptr为空
if (pre_ptr == NULL) {
// 如果cur_ptr也为空,说明这个链表就是空的,直接插入即可
if (cur_ptr == NULL) {
segregated_free_lists[pos] = ptr;
SET_PTR(GET_PRE_PTR(ptr), NULL);
SET_PTR(GET_SUCC_PTR(ptr), NULL);
} else {
// cur_ptr不为空,说明要插入的是第一个位置
segregated_free_lists[pos] = ptr;
SET_PTR(GET_PRE_PTR(cur_ptr), ptr);
SET_PTR(GET_SUCC_PTR(ptr), cur_ptr);
SET_PTR(GET_PRE_PTR(ptr), NULL);
}
// pre_ptr不为空
} else {
// 如果cur_ptr为空,说明是在链表尾部插入
if (cur_ptr == NULL) {
SET_PTR(GET_SUCC_PTR(pre_ptr), ptr);
SET_PTR(GET_PRE_PTR(ptr), pre_ptr);
SET_PTR(GET_SUCC_PTR(ptr), NULL);
} else {
// 如果cur_ptr不为空,说明是在链表中间插入
SET_PTR(GET_SUCC_PTR(pre_ptr), ptr);
SET_PTR(GET_PRE_PTR(ptr), pre_ptr);
SET_PTR(GET_SUCC_PTR(ptr), cur_ptr);
SET_PTR(GET_PRE_PTR(cur_ptr), ptr);
}
}
}
static void *coalesce(void *bp)
static void *coalesce(void *bp) {
// 首先获取前后内存块的状态
int is_pre_alloc = GET_ALLOC(HDRP(PREV_BLKP(bp)));
int pre_size = GET_SIZE(HDRP(PREV_BLKP(bp)));
int is_next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
int next_size = GET_SIZE(HDRP(NEXT_BLKP(bp)));
int cur_size = GET_SIZE(HDRP(bp));
// 根据状态分类讨论
int new_size;
// 前后均分配了
if (is_pre_alloc && is_next_alloc) {
return bp;
} else if (is_pre_alloc && !is_next_alloc) {
// 前面分配了,后面没分配,所以和后面合并
// 首先,在空闲块数组中删除这两个块
delete_node(bp);
delete_node(NEXT_BLKP(bp));
// 然后修改首部和尾部
new_size = cur_size + next_size;
} else if (!is_pre_alloc && is_next_alloc) {
delete_node(bp);
delete_node(PREV_BLKP(bp));
new_size = pre_size + cur_size;
// 修改当前的bp,因为和前面合并了,现在的bp应该指向前面的块的头部
bp = PREV_BLKP(bp);
} else {
// 前后都是空的
delete_node(bp);
delete_node(PREV_BLKP(bp));
delete_node(NEXT_BLKP(bp));
new_size = pre_size + cur_size + next_size;
bp = PREV_BLKP(bp);
}
// 修改当前合并后的空闲块的首部和尾部
PUT(HDRP(bp), PACK(new_size, 0));
// 只要设置好了头部,那么尾部就可以直接操作
PUT(FTRP(bp), PACK(new_size, 0));
// 将这个空闲块插入空闲块数组
insert_node(bp, new_size);
return bp;
}
void *mm_malloc(size_t size)
2*DSIZE
place
操作完成)void *mm_malloc(size_t size) {
// 首先调整size为合法值,最小为2*DSIZE,否则一定要是8的倍数
if (size == 0) {
return NULL;
} else if (size <= DSIZE) {
size = 2 * DSIZE;
} else {
size = ALIGN(size + DSIZE);
}
// 根据size去空闲块数组里找最合适的那个
int pos = find_pos(size);
void *fit_ptr = NULL;
while (pos < LIST_MAX_SIZE) {
// 去当前项里面找
void *cur_ptr = segregated_free_lists[pos];
while (cur_ptr != NULL) {
if (GET_SIZE(HDRP(cur_ptr)) < size) {
cur_ptr = GET_SUCC(cur_ptr);
} else {
fit_ptr = cur_ptr;
break;
}
}
if (fit_ptr != NULL) {
break;
}
pos++;
}
// 如果没有足够大的,说明堆要扩充空间了
if (fit_ptr == NULL) {
if ((fit_ptr = extend_heap(MAX(size, CHUNKSIZE))) == NULL) {
return NULL;
}
}
// 在该空闲块中分配size大小的块
fit_ptr = place(fit_ptr, size);
return fit_ptr;
}
static void *place(void *bp, size_t size)
static void *place(void *bp, size_t size) {
// 在bp中分配size大小的空闲块走
// 先在数组中删除bp空闲块
delete_node(bp);
// 获得bp块的长度
size_t free_size = GET_SIZE(HDRP(bp));
// 如果剩下的小于2*DSIZE,那就不用再插入回去了
if (free_size - size < 2 * DSIZE) {
PUT(HDRP(bp), PACK(free_size, 1));
PUT(FTRP(bp), PACK(free_size, 1));
} else if (size >= 96) {
PUT(HDRP(bp), PACK(free_size - size, 0));
PUT(FTRP(bp), PACK(free_size - size, 0));
insert_node(bp, free_size - size);
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(size, 1));
PUT(FTRP(bp), PACK(size, 1));
} else {
// 注意,这里是把前半部分给分配了,不能修改bp,最后还是要返回bp
// 前半部分要分配,后半部分重新插入
PUT(HDRP(bp), PACK(size, 1));
PUT(FTRP(bp), PACK(size, 1));
// 修改bp指向后半段
PUT(HDRP(NEXT_BLKP(bp)), PACK(free_size - size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(free_size - size, 0));
// 重新放入空闲块数组
insert_node(NEXT_BLKP(bp), free_size - size);
}
return bp;
}
void mm_free(void *ptr)
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);
}
void *mm_realloc(void *ptr, size_t size)
void *mm_realloc(void *ptr, size_t size) {
// 首先检查size的合法性
if (size == 0) {
return NULL;
}
// 修改size使其对齐
if (size <= DSIZE) {
size = 2 * DSIZE;
} else {
size = ALIGN(size + DSIZE);
}
// 计算当前块的大小与要求的size的差值
int cur_size = GET_SIZE(HDRP(ptr));
int change_size = cur_size - size;
// 如果size小于等于当前长度,则不需要重新分配
if (change_size >= 0) {
return ptr;
}
// 如果当前块后面就是结尾
if (GET_SIZE(HDRP(NEXT_BLKP(ptr))) == 0) {
// 扩展
if (extend_heap(MAX(change_size, CHUNKSIZE)) == NULL) {
return NULL;
}
// 扩展成功,修改头尾部
delete_node(NEXT_BLKP(ptr));
PUT(HDRP(ptr), PACK(cur_size + MAX(change_size, CHUNKSIZE), 1));
PUT(FTRP(ptr), PACK(cur_size + MAX(change_size, CHUNKSIZE), 1));
return ptr;
}
// 如果当前块后面有个free块,尝试去合并,看看是不是可以
if (!GET_ALLOC(HDRP(NEXT_BLKP(ptr)))) {
// 如果加起来长度够的话
int next_size = GET_SIZE(HDRP(NEXT_BLKP(ptr)));
if (cur_size + next_size >= size) {
delete_node(NEXT_BLKP(ptr));
PUT(HDRP(ptr), PACK(cur_size + next_size, 1));
PUT(FTRP(ptr), PACK(cur_size + next_size, 1));
return ptr;
}
}
// 最后一步了,只能去重新申请
void *new_ptr = mm_malloc(size);
memcpy(new_ptr, ptr, cur_size);
mm_free(ptr);
return new_ptr;
}