csapp buffer
/*
* 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 */
"TheDa",
/* First member's full name */
"Wuruohan",
/* First member's email address */
"1150562000@qq.com",
/* 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
//对齐字节数目
#define ALIGN(size) ((((size) + (ALIGNMENT-1)) /(ALIGNMENT))*(ALIGNMENT))
//对齐宏定义
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define WSIZE 4
//32位处理器中一个字的大小是4B
#define DSIZE 8
//32位处理器中一个双字的大小是8B
/* 每次扩展堆的块大小(系统调用“费时费力”,一次扩展一大块,然后逐渐利用这一大块) */
#define INITCHUNKSIZE (1<<6)
//堆的初始大小
#define CHUNKSIZE (1<<12)
//扩展堆的最小大小
#define LISTMAX 16
//这里的空闲块链表,按照1,2,4,8的大小分类,最大2的32次方大小
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define PACK(size, alloc) ((size) | (alloc))
//size是block大小,alloc是block的分配属性,这两个信息一起设置
#define GET(p) (*(unsigned int *)(p))
//读取位于p指针位置的一个字
#define PUT(p, val) (*(unsigned int *)(p) = (val))
//将val的值写入p指针位置
#define SET_PTR(p, ptr) (*(unsigned int *)(p) = (unsigned int)(ptr))
//p指针位置的值变成指针ptr指向的地址
#define GET_SIZE(p) (GET(p) & ~0x7)
//得知这个block的大小
#define GET_ALLOC(p) (GET(p) & 0x1)
//得知这个block是否被分配了
#define HDRP(ptr) ((char *)(ptr) - WSIZE)
//ptr可以是当前block的首地址,得到当前block的首地址
#define FTRP(ptr) ((char *)(ptr) + GET_SIZE(HDRP(ptr)) - DSIZE)
//ptr可以是当前block的首地址,得到当前block的尾部地址
#define NEXT_BLKP(ptr) ((char *)(ptr) + GET_SIZE((char *)(ptr) - WSIZE))
//ptr可以是当前block的首地址,ptr-4B指向的位置是当前block的头信息,因此得到下一个block的首地址
#define PREV_BLKP(ptr) ((char *)(ptr) - GET_SIZE((char *)(ptr) - DSIZE))
//ptr可以是当前block的首地址,ptr-4B*2指向的位置是上一个block的尾部信息,因此得到上一个block的首地址
#define PRED_PTR(ptr) ((char *)(ptr))
//显示空闲链表祖先指针
#define SUCC_PTR(ptr) ((char *)(ptr) + WSIZE)
//显示空闲链表后继指针,向低地址方向,就是开头空闲链表方向
#define PRED(ptr) (*(char **)(ptr))
//
#define SUCC(ptr) (*(char **)(SUCC_PTR(ptr)))
//
void *segregated_free_lists[LISTMAX];//空闲链表,在这里遍历空闲块
static void *extend_heap(size_t size);//扩展推
static void *coalesce(void *ptr);//合并相邻的Free block
static void *place(void *ptr, size_t size);//在prt所指向的free block块中allocate size大小的块,如果剩下的空间大于2*DWSIZE,则将其分离后放入Free list
static void insert_node(void *ptr, size_t size);//将ptr所指向的free block插入到分离空闲表中
static void delete_node(void *ptr);//将ptr所指向的块从分离空闲表中删除
static void *extend_heap(size_t size)//扩展堆,扩展大小为Size的堆空间,返回堆扩展得到的空闲block的ptr
{
void *ptr;
size = ALIGN(size);//首先对齐内存
if ((ptr = mem_sbrk(size)) == (void *)-1)//系统调用“sbrk”,看看是否能够成功扩大这么大的堆空间
//如果成功的话返回新的堆上界地址brk(堆是向上生长的,高字节方向)
return NULL;
PUT(HDRP(ptr), PACK(size, 0));//设置头部大小是size,属性为空闲
PUT(FTRP(ptr), PACK(size, 0));//设置尾部大小是size,属性为空闲
PUT(HDRP(NEXT_BLKP(ptr)), PACK(0, 1));// 注意这个块是堆的结尾,设置一下结尾块 ,头尾信息结构
insert_node(ptr, size);/* 设置好后将其插入到分离空闲表中 */
return coalesce(ptr);/* 另外这个free块的前面也可能是一个free块,可能需要合并 */
}
static void insert_node(void *ptr, size_t size)//将ptr指向的大小为size的block插入空闲链表segregated_free_lists当中
{
int listnumber = 0;
void *search_ptr = NULL;
void *insert_ptr = NULL;
while ((listnumber < LISTMAX - 1) && (size > 1))/* 通过块的大小找到对应的链 */
{
size >>= 1;
listnumber++;
}
/* 找到对应的链后,在该链中继续寻找对应的插入位置,以此保持链中块由小到大的特性 */
search_ptr = segregated_free_lists[listnumber];
while ((search_ptr != NULL) && (size > GET_SIZE(HDRP(search_ptr))))
{
insert_ptr = search_ptr;
search_ptr = PRED(search_ptr);//insert_ptr的后一个指针是search_ptr
}
//最终得到的插入位置是insert_ptr,分情况将该block插入该空闲块链表当中
if (search_ptr != NULL)//说明插入位置的后一个块不是空,插入位置不是最后一个
{
if (insert_ptr != NULL)//在中间插入
{
//注意隐式空闲链表和显式空闲链表
SET_PTR(PRED_PTR(ptr), search_ptr);//插入块高地址方向是search_ptr
SET_PTR(SUCC_PTR(search_ptr), ptr);//
SET_PTR(SUCC_PTR(ptr), insert_ptr);//插入块低地址方向是insert_ptr
SET_PTR(PRED_PTR(insert_ptr), ptr);//
}
else//插入位置的后面有block,插入指针为NULL,那么只能是在开头位置插入了
{
SET_PTR(PRED_PTR(ptr), search_ptr);
SET_PTR(SUCC_PTR(search_ptr), ptr);
SET_PTR(SUCC_PTR(ptr), NULL);
segregated_free_lists[listnumber] = ptr;//设置该条空闲链首指针
}
}
else
{
if (insert_ptr != NULL)//在结尾插入
{
SET_PTR(PRED_PTR(ptr), NULL);
SET_PTR(SUCC_PTR(ptr), insert_ptr);
SET_PTR(PRED_PTR(insert_ptr), ptr);
}
else//该链为空,这是第一次插入
{
SET_PTR(PRED_PTR(ptr), NULL);
SET_PTR(SUCC_PTR(ptr), NULL);
segregated_free_lists[listnumber] = ptr;
}
}
}
static void delete_node(void *ptr)//将ptr指向的block从空闲块链表删除
{
int listnumber = 0;
size_t size = GET_SIZE(HDRP(ptr));//得到当前块大小
while ((listnumber < LISTMAX - 1) && (size > 1))//通过块的大小找到对应的空闲链
{
size >>= 1;
listnumber++;
}
if (PRED(ptr) != NULL)//高地址方向,也就是后继块不为空
{
if (SUCC(ptr) != NULL)//前向block不为空,中间删除
{
SET_PTR(SUCC_PTR(PRED(ptr)), SUCC(ptr));
SET_PTR(PRED_PTR(SUCC(ptr)), PRED(ptr));
}
else//前向block为空,删除空闲块链开头block
{
SET_PTR(SUCC_PTR(PRED(ptr)), NULL);
segregated_free_lists[listnumber] = PRED(ptr);
}
}
else//前向block为空,结尾删除
{
if (SUCC(ptr) != NULL)//低地址方向,也就是前向块不为空
{
SET_PTR(PRED_PTR(SUCC(ptr)), NULL);
}
else//整条空闲链就这一个block
{
segregated_free_lists[listnumber] = NULL;
}
}
}
static void *coalesce(void *ptr)//尽可能合并ptr指向的block周围的空闲块,mm_free调用,堆扩展调用,返回新block的ptr
{
_Bool is_prev_alloc = GET_ALLOC(HDRP(PREV_BLKP(ptr)));//低地址方向block分配了吗
_Bool is_next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(ptr)));//高地址方向block分配了吗
size_t size = GET_SIZE(HDRP(ptr));//当前block大小
/* 另外注意到由于我们的合并和申请策略,不可能出现两个相邻的free块 */
if (is_prev_alloc && is_next_alloc)//前后block均被分配了
{
return ptr;
}
else if (is_prev_alloc && !is_next_alloc)//只有低地址方向block被分配,高地址方向未分配
{
delete_node(ptr);//合并前先删除该空闲块
delete_node(NEXT_BLKP(ptr));//删除高地址方向空闲块
size += GET_SIZE(HDRP(NEXT_BLKP(ptr)));//修改size
PUT(HDRP(ptr), PACK(size, 0));//新block的ptr位置就是ptr
PUT(FTRP(ptr), PACK(size, 0));//注意size已经改变了,FTRP可以正常使用
}
else if (!is_prev_alloc && is_next_alloc)//只有高地址方向block被分配,低地址方向未分配
{
delete_node(ptr);//合并前先删除该空闲块
delete_node(PREV_BLKP(ptr));//删除低地址方向空闲块
size += GET_SIZE(HDRP(PREV_BLKP(ptr)));//修改size
PUT(FTRP(ptr), PACK(size, 0));//新block的脚部分位于原ptr的foot
PUT(HDRP(PREV_BLKP(ptr)), PACK(size, 0));//新block的头部分位于低地址方向空闲块ptr的头
ptr = PREV_BLKP(ptr);//ptr变为低地址方向空闲块ptr
}
else//前后两个块都是free块,这时将三个块同时合并
{
delete_node(ptr);
delete_node(PREV_BLKP(ptr));
delete_node(NEXT_BLKP(ptr));
size += GET_SIZE(HDRP(PREV_BLKP(ptr))) + GET_SIZE(HDRP(NEXT_BLKP(ptr)));
PUT(HDRP(PREV_BLKP(ptr)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(ptr)), PACK(size, 0));
ptr = PREV_BLKP(ptr);
}
insert_node(ptr, size);//将合并好的free块加入到空闲链接表中
return ptr;
}
//在ptr所指向的空闲块中插入size大小的块,如果剩下的空间大于2*DWSIZE,则将其分离后放入Free list,返回最终块被分配的位置
static void *place(void *ptr, size_t size)
{
size_t ptr_size = GET_SIZE(HDRP(ptr));
size_t remainder = ptr_size - size;//在ptr所指向的空闲块中插入size大小的块后剩余的空间大小
delete_node(ptr);//从空闲链表当中删除ptr
if (remainder < DSIZE * 2)//如果剩余的大小小于最小块,只需要设置ptr指向block被分配即可
{
PUT(HDRP(ptr), PACK(ptr_size, 1));
PUT(FTRP(ptr), PACK(ptr_size, 1));
}
else if (size >= 96)//消除外部碎片的策略,如果插入的是大块,从该空闲块ptr的高地址方向放起
{
PUT(HDRP(ptr), PACK(remainder, 0));//注意这一步已经修改了ptr所指向的未分配block的size
PUT(FTRP(ptr), PACK(remainder, 0));//新的空闲块就生成了
PUT(HDRP(NEXT_BLKP(ptr)), PACK(size, 1));//由于ptr的size已经修改,下一个分配block就可以这样访问,修改分配block的属性
PUT(FTRP(NEXT_BLKP(ptr)), PACK(size, 1));
insert_node(ptr, remainder);//将空闲块插入空闲块链
return NEXT_BLKP(ptr);
}
else//消除外部碎片的策略,如果插入的是小块,从该空闲块ptr的低地址方向放起
{
PUT(HDRP(ptr), PACK(size, 1));//ptr指向的位置放分配block
PUT(FTRP(ptr), PACK(size, 1));
PUT(HDRP(NEXT_BLKP(ptr)), PACK(remainder, 0));//原理同上
PUT(FTRP(NEXT_BLKP(ptr)), PACK(remainder, 0));
insert_node(NEXT_BLKP(ptr), remainder);
}
return ptr;
}
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)//首先开辟一个4*WSIZE大小的空间
return -1;
PUT(heap, 0);//堆的头部
PUT(heap + (1 * WSIZE), PACK(DSIZE, 1));//2*WSIZE的空block
PUT(heap + (2 * WSIZE), PACK(DSIZE, 1));
PUT(heap + (3 * WSIZE), PACK(0, 1));//堆的尾部块
if (extend_heap(INITCHUNKSIZE) == NULL)//扩展堆,得到初始大小的堆
return -1;
return 0;
}
void *mm_malloc(size_t size)//自动在堆上分配大小为size的block,并返回分配的block的起始地址
{
if (size == 0)
return NULL;
if (size <= DSIZE)//因为是按照双字对齐,如果大小小于DSiZE,那么分配DSIZE加上2*4B的头尾信息
{
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];
while ((ptr != NULL) && ((size > GET_SIZE(HDRP(ptr)))))//然后在该空闲块链表上遍历
{
ptr = PRED(ptr);
}
if (ptr != NULL)//找到了
break;
}
searchsize >>= 1;
listnumber++;
}
if (ptr == NULL)//没有合适大小的block那么我们就需要扩展堆的大小
{
if ((ptr = extend_heap(MAX(size, CHUNKSIZE))) == NULL)
return NULL;
}
/* 在free块中allocate size大小的块 */
ptr = place(ptr, size);
return ptr;
}
void mm_free(void *ptr)//释放ptr所指向的分配block
{
size_t size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));//修改该block的分配属性
PUT(FTRP(ptr), PACK(size, 0));
insert_node(ptr, size);
coalesce(ptr);//先插入,再合并
}
void *mm_realloc(void *ptr, size_t size)//将之前malloc分配的空间修改分配大小,返回重新分配的block的地址
{
void *new_block = ptr;
int remainder;
if (size == 0)
return NULL;
if (size <= DSIZE)//内存对齐
{
size = 2 * DSIZE;
}
else
{
size = ALIGN(size + DSIZE);
}
if ((remainder = GET_SIZE(HDRP(ptr)) - size) >= 0)//如果size小于原来块的大小,直接返回原来的块
{
return ptr;
}
//现在势必要借用虚拟内存了,size有点大
else if( (!GET_ALLOC(HDRP(NEXT_BLKP(ptr)))) && GET_SIZE(HDRP(NEXT_BLKP(ptr)))>0 )//如果后面的一个block是空闲块
{
if((remainder = GET_SIZE(HDRP(ptr)) + GET_SIZE(HDRP(NEXT_BLKP(ptr))) - size)>=0)//如果加上后面的空闲块可以完成
{
delete_node(NEXT_BLKP(ptr));
PUT(HDRP(ptr), PACK(size , 1));
PUT(FTRP(ptr), PACK(size , 1));
PUT(HDRP(NEXT_BLKP(ptr)), PACK(remainder, 0));
PUT(FTRP(NEXT_BLKP(ptr)), PACK(remainder, 0));
insert_node(NEXT_BLKP(ptr), remainder);
}
else//如果加上后面的空闲块不能完成,空闲块后面可能是利用块,也可能是结尾,是结尾的情况直接扩展堆会包含,因为扩展堆会合并空闲块
{
if( !GET_SIZE(HDRP(NEXT_BLKP(NEXT_BLKP(ptr)))) )//如果后面一个空闲块的后面还是结尾
{
remainder=size-GET_SIZE(HDRP(ptr))-GET_SIZE(HDRP(NEXT_BLKP(ptr)));//还差多少
if (extend_heap(MAX(remainder, CHUNKSIZE)) == NULL)
return NULL;
delete_node(NEXT_BLKP(ptr));//后面一个是彻底被吸收了
remainder=MAX(remainder, CHUNKSIZE);
PUT(HDRP(ptr), PACK(GET_SIZE(HDRP(ptr)) + GET_SIZE(HDRP(NEXT_BLKP(ptr))) + remainder, 1));
PUT(FTRP(ptr), PACK(GET_SIZE(HDRP(ptr)) + GET_SIZE(HDRP(NEXT_BLKP(ptr))) + remainder, 1));
}
else//那么就不能利用后面的空闲块了
{
new_block = mm_malloc(size);
memcpy(new_block, ptr, GET_SIZE(HDRP(ptr)));
mm_free(ptr);
}
}
}
else if(!GET_SIZE(HDRP(NEXT_BLKP(ptr))) )//如果后一个block就是空,当前是结尾
{
remainder=size-GET_SIZE(HDRP(ptr));//还差多少
if (extend_heap(MAX(remainder, CHUNKSIZE)) == NULL)
return NULL;
remainder=MAX(remainder, CHUNKSIZE);
PUT(HDRP(ptr), PACK(GET_SIZE(HDRP(ptr)) + remainder, 1));
PUT(FTRP(ptr), PACK(GET_SIZE(HDRP(ptr))+ remainder, 1));
}
/*
else if (!GET_ALLOC(HDRP(NEXT_BLKP(ptr))) || !GET_SIZE(HDRP(NEXT_BLKP(ptr))))//如果高地址方向的下一块没有被分配,或者没有下一块了,这个块已经结束了
{
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);
}
delete_node(NEXT_BLKP(ptr));
PUT(HDRP(ptr), PACK(size + remainder, 1));
PUT(FTRP(ptr), PACK(size + remainder, 1));
}
//没有可以利用的连续free块,而且size大于原来的块,这时只能申请新的不连续的free块、复制原块内容、释放原块
*/
else//直接选择malloc一个新的出来
{
new_block = mm_malloc(size);
memcpy(new_block, ptr, GET_SIZE(HDRP(ptr)));
mm_free(ptr);
}
return new_block;
}