这里我们使用“隐式链表”的方法实现对“堆”的分配。
而且分配的单位是“字”,这里的字是4个字节,而且我们分配的内存都按8字节(也就是双字)对齐。
上图中一个空格代表一个字(4字节)
也就是我们的堆开始三个字是堆的对齐和头部用的。最后是堆的尾。
上图是我们堆的“分配块”的头部,由于我们的堆是以8字节对齐的,也就是分配的最小单位将是双字,于是我们知道对于长度来讲
最后三个位是没用的。因为必须是8的倍数嘛,于是我们可以用这几位来作标记看是否己经分配了该块。
块的头部和尾部是一样的。
我们这里采用“首次匹配”策略进行块的分配。也就是说我们从“堆”的头部开始搜索合适的块,当有块的大小大于要求的块大小时,我们就将当空的空块分割一块出来分配给它。
当整个“堆”没有合适的块时,我们在“堆”的后面申请新的块。
#ifndef DYNAMIC_MALLOC_H
#define DYNAMIC_MALLOC_H
#define NoFault 0
#define OutofMemory 1
#define NoRest 2
class DynamicMalloc{
protected:
const static int WSIZE=4;
const static int DSIZE=8;
const static bool ALLOCED=1;
const static bool UNALLOCED=0;
private:
static char* mem_heap; //指向堆首地址
static char* mem_brk; //指向目前分配的“堆”的最后一个字的下一个
static char* mem_max_addr; //一直指向最大所能分配的地址,“堆大小”
static char* heap_listp; //一直指向序言块
const static unsigned int MAX_HEAP=2048;
const static unsigned int CHUNKSIZE=30;
const static unsigned int MINCHUNK=DSIZE*2+2*WSIZE;
unsigned int pack(unsigned int size,unsigned int alloc){
return size|alloc;
}
void put_data(void *ptr,unsigned int val){
*((unsigned int*)ptr)=val;
}
unsigned int get_data(void *ptr){
return *((unsigned int*)(ptr));
}
unsigned int get_size(void *ptr){//取出块大小
return get_data(ptr)&(~0x07);
}
bool get_alloced(void *ptr){//取出“分配”标志
return get_data(ptr)&0x01;
}
char* mem_head(void *ptr){//找到块的头部
return (char*)ptr-WSIZE;
}
char* mem_ftrp(void *ptr){ //找到块的尾部
return (char*)ptr+get_size((void*)mem_head(ptr));
}
char* next_blkb(void *ptr){//找到下一块
return (char*)ptr+get_size((void*)mem_head(ptr))+DSIZE;
}
char *pre_blkb(void *ptr){//找到前面块
return (char*)ptr-get_size((void*)((char*)ptr-DSIZE))-DSIZE;
}
void *mem_sbrk(unsigned int inc);
void mm_init();
void* extern_heap(unsigned int size);
void coalesce(void *ptr);
void clearBlock(void *ptr);
void* find_fit(unsigned int size);
public:
int mem_errno;
DynamicMalloc();
void* mm_malloc(unsigned int size);
void mm_free(void *ptr);
};
#endif
#include<stdio.h>
#include<memory>
#include<iostream>
#include"DynamicMalloc.h"
char * DynamicMalloc::heap_listp=NULL;
char * DynamicMalloc::mem_heap=NULL;
char * DynamicMalloc::mem_brk=NULL;
char * DynamicMalloc::mem_max_addr=NULL;
DynamicMalloc::DynamicMalloc(){
if(mem_heap==NULL){
mem_errno=NoFault;
mem_heap=(char*)malloc(WSIZE*MAX_HEAP*sizeof(char));
if(mem_heap==NULL){
mem_errno=OutofMemory;
std::cout<<"overflow"<<std::endl;
}
mem_brk=mem_heap;
mem_max_addr=mem_heap+WSIZE*MAX_HEAP*sizeof(char);
mm_init();
}
}
void * DynamicMalloc::mem_sbrk(unsigned int inc){
if(inc<0||(mem_heap+inc>mem_max_addr)){
mem_errno=OutofMemory;
std::cout<<"memory overflow!"<<std::endl;
return NULL ;
}
char *old_mem_brk=mem_brk;
mem_brk+=inc+DSIZE; //预留空间,用于头和尾
return (void*)old_mem_brk;
}
void DynamicMalloc::mm_init(){
if((heap_listp=(char*)mem_sbrk(2*WSIZE))==NULL){
return;
}
put_data(heap_listp,pack(0,ALLOCED));
put_data(heap_listp+WSIZE,pack(0,ALLOCED));
put_data(heap_listp+2*WSIZE,pack(0,ALLOCED));
put_data(heap_listp+3*WSIZE,pack(0,ALLOCED));
heap_listp+=2*WSIZE;
extern_heap(CHUNKSIZE);
}
void* DynamicMalloc::extern_heap(unsigned int size){
int words=(size%2)? (size+1)*WSIZE:(size)*WSIZE;
char *bp=NULL;
bp=(char*)mem_sbrk(words);
put_data(mem_head(bp),pack(words,UNALLOCED)); //块头
put_data(mem_ftrp(bp),pack(words,UNALLOCED)); //块脚
put_data(mem_ftrp(bp)+WSIZE,pack(0,ALLOCED)); //新的“堆脚”,也是结束的标志
coalesce(bp);
return bp;
}
void DynamicMalloc::clearBlock(void *ptr){
char *clear_ptr=(char*)ptr;
for(int i=0;i<WSIZE; i++){
*(clear_ptr+i)=0;
}
}
void DynamicMalloc::coalesce(void *ptr){
bool preAlloced=ALLOCED;
bool nxtAlloced=ALLOCED;
preAlloced=get_alloced(mem_head(pre_blkb(ptr))); //获得前一块的状态
nxtAlloced=get_alloced(mem_head(next_blkb(ptr))); //获得下一块的状态
if(preAlloced&&nxtAlloced){//前后两块都分配了
return;
}else if(preAlloced&&!nxtAlloced){//前一块己分配,后一块没有
unsigned int new_size=get_size(mem_head(ptr))+get_size(mem_head(next_blkb(ptr)))+DSIZE;
put_data(mem_head(ptr),pack(new_size,UNALLOCED));//设置新块头部
put_data(mem_ftrp(ptr),pack(new_size,UNALLOCED)); //新块的脚
//clearBlock(mem_ftrp(ptr));
//clearBlock(mem_head(next_blkb(ptr))); //清理门户
}else if(!preAlloced&&nxtAlloced){//前面的未分配,后面的分配了
unsigned int new_size=get_size(mem_head(ptr))+get_size(mem_head(pre_blkb(ptr)))+DSIZE;
put_data(mem_head(pre_blkb(ptr)),pack(new_size,UNALLOCED));
put_data(mem_ftrp(ptr),pack(new_size,UNALLOCED));
//clearBlock(mem_head(ptr));
//clearBlock(mem_ftrp(pre_blkb(ptr)));
}else if(!preAlloced&&!nxtAlloced){//前后都未分配
unsigned int new_size=get_size(mem_head(ptr))+get_size(mem_head(pre_blkb(ptr)))+get_size(mem_head(next_blkb(ptr)));
put_data(mem_head(pre_blkb(ptr)), pack(new_size,UNALLOCED));
put_data(mem_ftrp(next_blkb(ptr)),pack(new_size,UNALLOCED));
//clearBlock(mem_head(ptr));
//clearBlock(mem_ftrp(ptr));
//clearBlock(mem_ftrp(pre_blkb(ptr)));
//clearBlock(mem_head(next_blkb(ptr))); //清理门户
}
}
void* DynamicMalloc::mm_malloc(unsigned int size){
unsigned int malloc_size=0;
void *reptr=NULL;
if(size==0){
return NULL;
}
if(size<=DSIZE){
malloc_size=DSIZE;
}else{
malloc_size=DSIZE*((size+DSIZE+DSIZE-1)/DSIZE);
}
if((reptr=find_fit(malloc_size))!=NULL){
return (void*)reptr;
}else{
reptr=(void*)extern_heap(malloc_size);
put_data(mem_head(reptr),pack(malloc_size,ALLOCED));
put_data(mem_ftrp(reptr),pack(malloc_size,ALLOCED));
return reptr;
}
}
void* DynamicMalloc::find_fit(unsigned int size){
char *ptr=next_blkb(heap_listp); //heap_listp一直指向序言块
std::cout<<get_size(mem_head(ptr))<<std::endl;
std::cout<<get_alloced(mem_head(ptr))<<std::endl;
while(((get_size(mem_head(ptr))!=0)||(get_alloced(mem_head(ptr)))!=ALLOCED)){//从前往后 “首次匹配”
std::cout<<get_size(mem_head(ptr))<<std::endl;
std::cout<<get_alloced(mem_head(ptr))<<std::endl;
if(((get_size(mem_head(ptr))>=size)&&(get_alloced(mem_head(ptr)))==UNALLOCED)){
break;;
}
ptr=next_blkb(ptr);
}
if(get_alloced(mem_head(ptr))==ALLOCED&&get_size(mem_head(ptr))==0){//没有合适的空间
mem_errno=NoRest;
return NULL;
}else{
if(get_size(mem_head(ptr))>=size+MINCHUNK){
unsigned int old_size=get_size(mem_head(ptr));
put_data(mem_head(ptr),pack(size,ALLOCED)); //待分配块“头”
put_data(mem_ftrp(ptr),pack(size,ALLOCED)); //待分配块“尾”
put_data(mem_head(next_blkb(ptr)),pack(old_size-size-2*WSIZE,UNALLOCED));
put_data(mem_ftrp(next_blkb(ptr)),pack(old_size-size-2*WSIZE,UNALLOCED));
return ptr;
}else{
return ptr;
}
}
}
void DynamicMalloc::mm_free(void *ptr){
put_data(mem_head(ptr),pack(get_size(mem_head(ptr)),UNALLOCED));
put_data(mem_ftrp(ptr),pack(get_size(mem_head(ptr)),UNALLOCED));
coalesce(ptr);
}
#include "DynamicMalloc.h"
#include<iostream>
int main(){
DynamicMalloc dynamicMalloc;
char *ptr=(char*)dynamicMalloc.mm_malloc(10);
for(int i=0;i<10;i++){
*(ptr+i)='a';
}
for(int i=0;i<10;i++){
std::cout<<*(ptr+i)<<std::endl;
}
char* p=(char*)dynamicMalloc.mm_malloc(20);
for(int i=0;i<20;i++){
*(p+i)='a';
}
for(int i=0;i<20;i++){
std::cout<<*(p+i)<<std::endl;
}
char* pp=(char*)dynamicMalloc.mm_malloc(300);
dynamicMalloc.mm_free(p);
}
对于空相邻空闲块的管理策略:
也就是这部分的代码:
void DynamicMalloc::coalesce(void *ptr){
bool preAlloced=ALLOCED;
bool nxtAlloced=ALLOCED;
preAlloced=get_alloced(mem_head(pre_blkb(ptr))); //获得前一块的状态
nxtAlloced=get_alloced(mem_head(next_blkb(ptr))); //获得下一块的状态
if(preAlloced&&nxtAlloced){//前后两块都分配了
return;
}else if(preAlloced&&!nxtAlloced){//前一块己分配,后一块没有
unsigned int new_size=get_size(mem_head(ptr))+get_size(mem_head(next_blkb(ptr)))+DSIZE;
put_data(mem_head(ptr),pack(new_size,UNALLOCED));//设置新块头部
put_data(mem_ftrp(ptr),pack(new_size,UNALLOCED)); //新块的脚
//clearBlock(mem_ftrp(ptr));
//clearBlock(mem_head(next_blkb(ptr))); //清理门户
}else if(!preAlloced&&nxtAlloced){//前面的未分配,后面的分配了
unsigned int new_size=get_size(mem_head(ptr))+get_size(mem_head(pre_blkb(ptr)))+DSIZE;
put_data(mem_head(pre_blkb(ptr)),pack(new_size,UNALLOCED));
put_data(mem_ftrp(ptr),pack(new_size,UNALLOCED));
//clearBlock(mem_head(ptr));
//clearBlock(mem_ftrp(pre_blkb(ptr)));
}else if(!preAlloced&&!nxtAlloced){//前后都未分配
unsigned int new_size=get_size(mem_head(ptr))+get_size(mem_head(pre_blkb(ptr)))+get_size(mem_head(next_blkb(ptr)));
put_data(mem_head(pre_blkb(ptr)), pack(new_size,UNALLOCED));
put_data(mem_ftrp(next_blkb(ptr)),pack(new_size,UNALLOCED));
//clearBlock(mem_head(ptr));
//clearBlock(mem_ftrp(ptr));
//clearBlock(mem_ftrp(pre_blkb(ptr)));
//clearBlock(mem_head(next_blkb(ptr))); //清理门户
}
}
这是开始的一些字段初使化:
static char* mem_heap; //指向堆首地址
static char* mem_brk; //指向目前分配的“堆”的最后一个字的下一个
static char* mem_max_addr; //一直指向最大所能分配的地址,“堆大小”
static char* heap_listp; //一直指向序言块