介绍
这里采用的是分离适配的方法,共有9条空闲链表,分别存储大小不同的块。
将9个链表的头部地址按照数组的方式存储,如下图:
每个链表内的块组织方式如下:
作为空闲块时,bp内部的头两个字节被用作连接链表的next的prev,分配给用户后,这两部分无需使用,故也被分配出去。
malloc策略是,从某一条链表开始找,首次适配。如果这一条链没有合适的块,则在下一条更大的链寻找,直到所有链都寻找完。如果找到了合适的块,则选择性的将剩余部分分裂出去,重新插入到某一条链中。如果没有找到合适的块,则扩展堆,然后再次寻找。
free策略是,将指定的块free,并在物理空间上向前向后合并,最后插入到合适的链表中。
realloc策略是,如果size比原来小,则在原来的空间上缩减;如果比原来大,则尝试向后扩展;如果空间仍然不足,则重新分配。
代码
/*
* mm-naive.c - The fastest, least memory-efficient malloc package.
*
* In this naive approach, a block is allocated by simply incrementing
* the brk pointer. A block is pure payload. There are no headers or
* footers. Blocks are never coalesced or reused. Realloc is
* implemented directly using mm_malloc and mm_free.
*
* NOTE TO STUDENTS: Replace this header comment with your own header
* comment that gives a high level description of your solution.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include "mm.h"
#include "memlib.h"
/*********************************************************
* NOTE TO STUDENTS: Before you do anything else, please
* provide your team information in the following struct.
********************************************************/
team_t team = {
/* Team name */
"...",
/* First member's full name */
"Zzxy1999",
/* First member's email address */
"...",
/* Second member's full name (leave blank if none) */
"",
/* Second member's email address (leave blank if none) */
""
};
/* single word (4) or double word (8) alignment */
#define ALIGNMENT 8
/* rounds up to the nearest multiple of ALIGNMENT */
#define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7)
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
/* addr,size大小都是 32位 4个字节 */
#define WSIZE 4
/* 每次extend的大小 */
#define CHUNKSIZE (1 << 14)
/* 各个链表内的块大小分别为 [16, 32) [32, 64)... [1 << 12, ...) */
#define MIN_BLOCK_SIZE (1 << 4)
#define MAX_BLOCK_SIZE (1 << 12)
/* 剩余块达到一定大小才进行split */
#define SPLIT_SIZE (MIN_BLOCK_SIZE << 3)
/* 9条链表 */
#define LIST_ARRAY_SIZE 9
#define PACK(size, alloc) (size | alloc)
/* 在p地址存取32位数据 */
#define PUT(p, val) (*((unsigned int*)(p)) = val)
#define GET(p) (*((unsigned int*)(p)))
/* bp为返回给用户的堆地址 */
#define HEAD(bp) (((char* )(bp)) - WSIZE)
#define GET_SIZE(bp) (GET(HEAD(bp)) & ~0x7)
#define GET_ALLOC(bp) (GET(HEAD(bp)) & 0x1)
#define FOOT(bp) (((char* )(HEAD(bp))) + GET_SIZE(bp) - WSIZE)
/* 由bp获取存储next和prev的位置 */
#define NEXT_PTR(bp) (bp)
#define PREV_PTR(bp) (((char* )(bp)) + WSIZE)
/* 获取next和prev */
#define NEXT_BP(bp) (GET(NEXT_PTR(bp)))
#define PREV_BP(bp) (GET(PREV_PTR(bp)))
/* 设置bp的next和prev */
#define SET_NEXT(bp, next) (PUT(NEXT_PTR(bp), next))
#define SET_PREV(bp, prev) (PUT(PREV_PTR(bp), prev))
#define MAX(m, n) (m > n ? m : n)
/* 每个链表的头节点组合为一个数组*/
static char* list_array = NULL;
/* 分配给用户的最小地址和最大地址 */
static char* min_heap = NULL;
static char* max_heap = NULL;
void construct_block(char* bp, int size, int alloc);
char* find_list(int size);
void insert_block(char* bp);
void delete_block(char* bp);
int extend(int size);
char* find_block(char* list, int size);
void split(char* bp, int size);
void alloc(char* bp, int size);
char* mem_prev_bp(char* bp);
char* mem_next_bp(char* bp);
char* coalesce(char* ptr);
/*
* 初始化链表头的数组
* 初始化堆的最小和最大地址
* 首次extend
*/
int mm_init(void) {
if ((list_array = mem_sbrk(LIST_ARRAY_SIZE * WSIZE)) == (void* )-1) {
return -1;
}
/* 所有链表头指向NULL */
for (int i = 0; i < LIST_ARRAY_SIZE; ++i) {
PUT(list_array + i * WSIZE, 0);
}
min_heap = list_array + LIST_ARRAY_SIZE * WSIZE;
max_heap = list_array + LIST_ARRAY_SIZE * WSIZE;
if (extend(CHUNKSIZE) == -1) {
return -1;
}
return 0;
}
/*
* 寻找合适的块并分配
* 必要时extend
*/
void* mm_malloc(size_t size) {
size = ALIGN(size) + WSIZE * 2;
char* list = find_list(size);
char* bp = find_block(list, size);
/* 未找到合适的块,需要extend */
if (bp == NULL) {
/* extend的大小为CHUNKSIZE和size的较大者 */
if (extend(MAX(CHUNKSIZE, size)) == -1) {
return NULL;
}
/* extend成功,一定可以找到合适的bp */
bp = GET(list_array + WSIZE * (LIST_ARRAY_SIZE - 1));
}
alloc(bp, size);
return bp;
}
/*
* 合并 + 入链
*/
void mm_free(void* ptr) {
/* 合并并构造一个新的空闲块 */
void* bp = coalesce(ptr);
insert_block(bp);
}
/*
* 缩减原空间 / 合并后面的空间 / 分配新的空间
*/
void* mm_realloc(void* ptr, size_t size)
{
int new_size = ALIGN(size) + 2 * WSIZE;
int old_size = GET_SIZE(ptr);
/* 缩减原来的空间,如果剩余部分较大,则split */
if (old_size >= new_size) {
if (old_size - new_size >= SPLIT_SIZE) {
split(ptr, new_size);
coalesce((char*)(ptr) + new_size);
construct_block(ptr, new_size, 1);
}
return ptr;
}
/* 尝试与next块合并 */
char* next = mem_next_bp(ptr);
if (next != NULL && GET_ALLOC(next) == 0) {
int next_size = GET_SIZE(next);
int need = (new_size - old_size);
if (next_size >= need) {
delete_block(next);
/* next的剩余部分插入链表中 */
if (next_size - need >= MIN_BLOCK_SIZE) {
construct_block(ptr, new_size, 1);
construct_block(next + need, next_size - need, 0);
insert_block(next + need);
} else {
construct_block(ptr, old_size + next_size, 1);
}
return ptr;
}
}
/* 分配新的空间 */
void* dst = mm_malloc(new_size);
if (dst == NULL) {
return NULL;
}
int cpy_size = GET_SIZE(ptr) - 2 * WSIZE;
memcpy(dst, ptr, cpy_size);
mm_free(ptr);
return dst;
}
/*
* 以bp和size构造一个block的头和尾,alloc指定这个块的分配情况
*/
void construct_block(char* bp, int size, int alloc) {
PUT(HEAD(bp), PACK(size, alloc));
PUT(FOOT(bp), PACK(size, alloc));
}
/*
* 根据size,返回一个合适的链表头
*/
char* find_list(int size) {
/* 默认为最后一条链表 */
char* list = list_array + WSIZE * (LIST_ARRAY_SIZE - 1);
for (int i = 0; i < LIST_ARRAY_SIZE; ++i) {
if (size < (MIN_BLOCK_SIZE << (i + 1))) {
list = list_array + WSIZE * i;
return list;
}
}
return list;
}
/*
* 将一个block插入到合适的链表中
*/
void insert_block(char* bp) {
int size = GET_SIZE(bp);
char* list = find_list(size);
/* 插入到list的头部 */
void* tmp = GET(list);
SET_NEXT(bp, tmp);
SET_PREV(bp, list);
if (tmp != NULL) {
SET_PREV(tmp, bp);
}
PUT(list, bp);
}
/*
* 将一个block在list中删去
*/
void delete_block(char* bp) {
void* prev = PREV_BP(bp);
SET_NEXT(prev, NEXT_BP(bp));
if (NEXT_BP(bp) != NULL) {
SET_PREV(NEXT_BP(bp), prev);
}
}
/*
* 扩展size大小的空间,并插入相应的链表中
*/
int extend(int size) {
size = ALIGN(size);
char* p = mem_sbrk(size);
if (p == (void* )-1) {
return -1;
}
char* bp = p + WSIZE;
construct_block(bp, size, 0);
insert_block(bp);
max_heap += size;
return 0;
}
/*
* 在list中寻找合适的块,首次适配
* 如果list中没有,则在下一个的list中寻找
*/
char* find_block(char* list, int size) {
char* end = list_array + WSIZE * LIST_ARRAY_SIZE;
while (list != end) {
char* bp = GET(list);
while (bp != NULL) {
if (GET_SIZE(bp) >= size) {
return bp;
}
bp = NEXT_BP(bp);
}
/* 下一条链 */
list += WSIZE;
}
return NULL;
}
/*
* 对于bp块,分裂出两部分
* 第一部分的大小为size,分配给用户
* 第二部分为剩余部分,重新插入到合适的链表中
*/
void split(char* bp, int size) {
int remain_size = GET_SIZE(bp) - size;
char* remain_bp = bp + size;
construct_block(remain_bp, remain_size, 0);
insert_block(remain_bp);
}
/*
* 在bp这个块分配size的空间出去
* 选择性地对bp进行split
*/
void alloc(char* bp, int size) {
int bsize = GET_SIZE(bp);
if (bsize - size >= SPLIT_SIZE) {
split(bp, size);
construct_block(bp, size, 1);
} else {
construct_block(bp, bsize, 1);
}
delete_block(bp);
}
/*
* 物理空间中,bp前面的块
*/
char* mem_prev_bp(char* bp) {
if (bp - 2 * WSIZE < min_heap) {
return NULL;
}
return bp - ((GET(bp - 2 * WSIZE)) & ~0x7);
}
/*
* 物理空间中,bp后面的块
*/
char* mem_next_bp(char* bp) {
char* tmp = bp + GET_SIZE(bp);
if (tmp >= max_heap) {
return NULL;
}
return tmp;
}
/*
* 从bp开始,向前向后寻找空闲块,进行合并
*/
char* coalesce(char* ptr) {
/* 合并后的空闲块大小 */
int size = GET_SIZE(ptr);
/* 合并后的空闲块头部 */
void* bp = ptr;
/* 向前寻找 */
void* prev = mem_prev_bp(ptr);
while (prev != NULL && !GET_ALLOC(prev)) {
size += GET_SIZE(prev);
delete_block(prev);
bp = prev;
prev = mem_prev_bp(prev);
}
/* 向后寻找 */
void* next = mem_next_bp(ptr);
while (next != NULL && !GET_ALLOC(next)) {
size += GET_SIZE(next);
delete_block(next);
next = mem_next_bp(next);
}
construct_block(bp, size, 0);
return bp;
}