CSAPP MallocLab

介绍

这里采用的是分离适配的方法,共有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;
}

结果

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值