CSAPP Lab5: Malloc

本文为USTC SSE CSAPP 2020 Fall实验三的记录,仅供参考

PPT链接在这里:Lab 5.pptx  代码在这里 mm2.c mm3.c

版本一代码如下(无注释  注释请看版本二):

/*
 * 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 "memlib.h"
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
#include <assert.h>
#include <unistd.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 */
    "123456",
    /* First member's full name */
    "xxx xxx",
    /* First member's email address */
    "xxxx@mail.ustc.edu.cn",
    /* 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

// 注释请参照 mm3.c


/* rounds up to the nearest multiple of ALIGNMENT */
#define ALIGN(size) (((size) + (ALIGNMENT-1)) & (~(ALIGNMENT-1)))

#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define base_type size_t
#define base_pt base_type*
#define BASE_TYPE(e) ((base_type)e)
#define BASE_PT(e) ((base_pt)e)
#define BASE_TYPE_SIZE (ALIGN(sizeof(base_type)))

#define INITED ~0

#define OVHD_PREV_IDX -3
#define OVHD_NEXT_IDX -2
#define OVHD_SIZE_IDX -1
#define HD_INIT_IDX 0
#define FB_LIST_NUM 9
#define HD_N (FB_LIST_NUM + 1)
#define OVHD_N 3
#define HD_SIZE (BASE_TYPE_SIZE * HD_N)
#define OVHD_SIZE (BASE_TYPE_SIZE * OVHD_N)
#define TAIL_SIZE BASE_TYPE_SIZE
#define OV_SIZE (ALIGN(OVHD_SIZE + TAIL_SIZE))

#define inc_n_byte(p, n) ((base_pt)((((char*)(p)) + n)))
#define HEAP_START_PT (inc_n_byte(mem_heap_lo(), HD_SIZE))

#define calc_blcok_total_size(alloc_size) (OVHD_SIZE + ALIGN(alloc_size) + TAIL_SIZE)

#define BLOCK_USED 0
#define BLOCK_FREE 1
#define BLOCK_FLAG_BITS 1
#define BLOCK_FLAG_MASK ((1 << BLOCK_FLAG_BITS) - 1)

#define calc_size_val(size, v) ((size << BLOCK_FLAG_BITS) | v)

#define m_read(p, idx) (((base_pt)(p))[((base_type)(idx))])
#define m_write(p, idx, v) (m_read(p, idx) = ((base_type)v))

#define hd_read(idx) (m_read(mem_heap_lo(), idx))
#define hd_write(idx, v) (m_write(mem_heap_lo(), idx, v))

#define hd_inited (m_read(mem_heap_lo(), HD_INIT_IDX))

#define set_hd_inited() (m_write(mem_heap_lo(), HD_INIT_IDX, INITED))

#define block_flag(p) ((m_read(p, OVHD_SIZE_IDX)) & BLOCK_FLAG_MASK)
#define block_size(p) ((m_read(p, OVHD_SIZE_IDX)) >> BLOCK_FLAG_BITS)
#define block_last(p) (inc_n_byte(p, ((block_size(p)) - (OV_SIZE))))
#define block_next(p) (BASE_PT(m_read(p, OVHD_NEXT_IDX)))
#define block_prev(p) (BASE_PT(m_read(p, OVHD_PREV_IDX)))
#define block_content_pt(p) (inc_n_byte(p, OVHD_SIZE))

#define is_block_used(p) ((block_flag(p)) == (BLOCK_USED))
#define is_block_free(p) ((block_flag(p)) != (BLOCK_USED))

#define set_block_hd_size(p, v) (m_write(p, OVHD_SIZE_IDX, v))
#define set_block_td_size(p, v) (m_write(block_last(p), 0, v))
#define set_block_next(p, v) (m_write(p, OVHD_NEXT_IDX, v))
#define set_block_prev(p, v) (m_write(p, OVHD_PREV_IDX, v))

#define REMIAN_SIZE (ALIGN(sizeof(base_type) * 3))
#define EXTRA_SAPCE (ALIGN(REMIAN_SIZE + OV_SIZE))

#define print(m_type) (printf("sizeof(" #m_type "): %d\n", sizeof(m_type)))

#define DEBUG_MODE 0

void set_block_size(base_pt block, base_type total_size, base_type block_flag) {
    base_type size_val = calc_size_val(total_size, block_flag);
    set_block_hd_size(block, size_val);
    set_block_td_size(block, size_val);
}

#define set_block_used(block, total_size) (set_block_size(block, total_size, BLOCK_USED))
#define set_block_free(block, total_size) (set_block_size(block, total_size, BLOCK_FREE))
#define mark_block_used(block)  (set_block_used(block, block_size(block)))
#define mark_block_free(block)  (set_block_free(block, block_size(block)))

void px(void* p, int x) {
    for (int i = 0; i < x; i++) {
        printf("i:%d, addr: 0x%x, content:0x%x\n", i, &m_read(p, i), m_read(p, i));
    }  
}

void print_type_info() {
    printf("\n");
    print(char*);
    print(void*);
    print(base_pt);
    print(base_type);
    print(int);
    printf("\n");
}

base_type get_list_idx(base_type total_size) {
    base_type idx[FB_LIST_NUM - 1] = {48, 64, 128, 256, 512, 1024, 2072, 4104};
    base_type i = 0;
    for (; i < FB_LIST_NUM - 1; i++)
        if (total_size <= idx[i])
            break;
    return i + 1;
}

void insert_into_free_list_head(base_pt block) {
    base_type size = block_size(block);
    base_type list_idx = get_list_idx(size);
    // if (DEBUG_MODE)
    //     printf("insert block(addr:%p size:%d) into list %d\n", block, size, list_idx);
    set_block_free(block, size);       
    set_block_prev(block, NULL);
    set_block_next(block, NULL);
    base_pt head = BASE_PT(hd_read(list_idx));
    if (head == NULL) {
        hd_write(list_idx, block);
    }
    else {
        base_pt old_head = head;
        hd_write(list_idx, block);
        set_block_next(block, old_head);
        if (old_head)
            set_block_prev(old_head, block);
    }
}

void print_list() {
    // printf("\n");
    for (base_type i = 1; i <= FB_LIST_NUM; i++) {
        printf("list: idx=%d, element:", i);
        base_pt pt = hd_read(i);
        while(pt != NULL) {
            printf("[%p(%d:%d)]", pt, block_size(pt), block_flag(pt));
            pt = block_next(pt);
            if (pt)
                printf(" -> ");
        }
        printf("\n");
    }
    printf("\n");
}

base_pt search_free_list(base_type total_size) {
    base_type list_idx = get_list_idx(total_size);
    for (base_type idx = list_idx; idx <= FB_LIST_NUM; idx++) {
        base_pt pt = BASE_PT(hd_read(idx));
        while (pt && (block_size(pt) < total_size))
            pt = block_next(pt);
        if (pt) {
            if (DEBUG_MODE)
                printf("found free block(addr:%p, size:%d)\n", pt, block_size(pt));
            return pt;
        }
    }
    if (DEBUG_MODE)
        printf("found no free block\n");
    return NULL;
}

void remove_from_free_list(base_pt block) {
    base_type list_idx = get_list_idx(block_size(block));
    if (!block_prev(block))
        hd_write(list_idx, block_next(block));
    else
        set_block_next(block_prev(block), block_next(block));
        
    if (block_next(block))
        set_block_prev(block_next(block), block_prev(block));

    set_block_prev(block, NULL);
    set_block_next(block, NULL);
    mark_block_used(block);
    return block;
}


base_pt split_block(base_pt block, base_type total_size) {
    base_type size = block_size(block);
    if (is_block_used(block)) {
        printf("split block error: try to split an used block\n");
        return NULL;
    }

    if (size < total_size) {
        printf("split block error: try to split block into bigger blocks.\n");
        return NULL;
    }
    else {
        remove_from_free_list(block);
        if (size >= (total_size + EXTRA_SAPCE)) {
            base_pt new_block = inc_n_byte(block, total_size);
            base_pt list_idx = get_list_idx(size);
            
            set_block_used(block, total_size);
            set_block_used(new_block, size - total_size);

            if (DEBUG_MODE)
                printf("split free block(addr:%p, size:%d) into ret block(addr:%p, size:%d) and ", block, size, block, block_size(block));
            
            if (DEBUG_MODE)
                printf("new block(addr:%p, size:%d)\n", new_block, block_size(new_block));
            insert_into_free_list_head(new_block);
        }
        return block;
    }
}

void* true_malloc(base_type total_size) {
    base_pt *p = BASE_PT(mem_sbrk(total_size));
    // printf("info: true malloc has got a new block with addr: %p, size: %llu\n", p,  total_size);
    if (p == -1)
        return NULL;
    p = block_content_pt(p);
    set_block_next(p, NULL);
    set_block_prev(p, NULL);
    set_block_used(p, total_size);

    return (void*)p;
}

int mm_init (void) {
    // print_type_info();
    base_type r = BASE_TYPE(mem_sbrk(HD_SIZE));
    if (r == -1)
        return -1;
    set_hd_inited();
    for (base_type i = 1; i <= FB_LIST_NUM; i++)
        hd_write(i, NULL);
    // printf("low: 0x%x, high:0x%x\n", mem_heap_lo(), mem_heap_hi());
    // px(mem_heap_lo(), 5);
    return 0;
}

void * mm_malloc (size_t size) {
    if (!size)  return NULL;

    if (hd_inited != INITED) {
        printf("error: try to call malloc before inited.\n");
        return NULL;
    }
    base_type total_size = calc_blcok_total_size(size);
    base_pt pt = search_free_list(total_size);
    if (pt == NULL) {
        void *r =  true_malloc(total_size);
        if (DEBUG_MODE) {
            printf("malloc: request size: %d, allocated addr: %p, size: %d\n", size, r, block_size(r));
            print_list();
        }
        return r;
    }
    else {
        void *r = split_block(pt, total_size);
        if (DEBUG_MODE) {
            printf("malloc: request size: %d, allocated addr: %p, size: %d\n", size, r, block_size(r));
            print_list();
        }
        return r;
    }
}

void mm_free(void *ptr) {
    if (hd_inited != INITED) {
        printf("free rror: try to call free before inited.\n");
        return;
    }

    base_pt pt = BASE_PT(ptr);
    if (is_block_free(pt)) {
        printf("free error: try to free a free block with addr: %p\n", pt);
        return;
    }
    if (DEBUG_MODE)
        printf("free: try to free a block with addr:%p, size:%d\n", ptr, block_size(ptr));
    
    base_type size = block_size(pt);
    if (DEBUG_MODE)
        printf("-----------------free analysis for block(addr:%p, size:%d)------------------------\n", pt, size);
    base_type prev_block_size_val_pt = inc_n_byte(pt, -(OVHD_SIZE + BASE_TYPE_SIZE));
    if (DEBUG_MODE)
        printf("prev: size_val_pt:%p heap_start:%p\n", prev_block_size_val_pt, HEAP_START_PT);
    if (prev_block_size_val_pt > HEAP_START_PT) {
        base_type prev_block_size_val = m_read(pt, -(OVHD_N + 1));
        if (DEBUG_MODE)
            printf("prev_block_size_val:0x%x, flag:%x\n", prev_block_size_val, prev_block_size_val & BLOCK_FLAG_MASK);
        if ((prev_block_size_val & BLOCK_FLAG_MASK) != BLOCK_USED) {
            base_type prev_block_size = (prev_block_size_val >> BLOCK_FLAG_BITS);
            base_pt prev_block = inc_n_byte(pt, -prev_block_size);
            if (DEBUG_MODE)
                printf("merge current block(addr:%p size:%d) and prev block(addr:%p size:%d) into ", pt, size, prev_block, prev_block_size);
            remove_from_free_list(prev_block);
            size += prev_block_size;
            pt = prev_block;
            set_block_size(pt, size, BLOCK_USED);
            if (DEBUG_MODE)
                printf("new block(addr:%p, size:%d)\n", pt, block_size(pt));
        }
    }

    base_pt next_block = inc_n_byte(pt, size);
    if (DEBUG_MODE)
        printf("next: block_pt:%p heap_hi:%p\n", next_block, mem_heap_hi());
    if (next_block < mem_heap_hi()) {
        base_type next_block_size_val = m_read(next_block, OVHD_SIZE_IDX);
        if (DEBUG_MODE)
            printf("next_block_size_val:0x%x flag:%x\n", next_block_size_val, next_block_size_val & BLOCK_FLAG_MASK);
        if ((next_block_size_val & BLOCK_FLAG_MASK) != BLOCK_USED) {
            base_type next_block_size = (next_block_size_val >> BLOCK_FLAG_BITS);
            if (DEBUG_MODE)
                printf("merge current block(addr:%p size:%d) and next block(addr:%p size:%d) into", pt, size, next_block, next_block_size);
            remove_from_free_list(next_block);
            set_block_size(pt, size + next_block_size, BLOCK_USED);
            if (DEBUG_MODE)
                printf("new block(addr:%p, size:%d)\n", pt, block_size(pt));
        }
    }

    if (DEBUG_MODE)
        printf("----------------------------------free analysis end--------------------------------------\n", pt, size);

    insert_into_free_list_head(pt);

    if (DEBUG_MODE)
        print_list();
}

void *mm_realloc(void *ptr, size_t size) {
    if (hd_inited != INITED) {
        printf("error: try to call realloc before inited.\n");
        return NULL;
    }
    if (DEBUG_MODE)
        printf("realloc: try to realloc a block with addr:%p, size:%d\n", ptr, size);
    if (!ptr)   return mm_malloc(size);
    if (!size)  {
        mm_free(ptr);
        return NULL;
    }
    base_type blockSize = block_size(ptr);
    base_type adjusted_size = calc_blcok_total_size(size);
    if (adjusted_size <= blockSize) {
        if (DEBUG_MODE)
            print_list();
        return ptr;
    }
        
    else {
        base_pt new_ptr = mm_malloc(size);
        if (!new_ptr)   return NULL;
        memcpy(new_ptr, ptr, blockSize - OVHD_SIZE);
        mm_free(ptr);
        if (DEBUG_MODE)
            print_list();
        return new_ptr;
    }
}

版本二代码如下:

/*
 * 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 "memlib.h"
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
#include <assert.h>
#include <unistd.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 */
    "123456",
    /* First member's full name */
    "xxx xxx",
    /* First member's email address */
    "xxxx@mail.ustc.edu.cn",
    /* 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)) & (~(ALIGNMENT-1)))

// 一些常用的type和他们的size
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define base_type size_t
#define base_pt base_type*
#define BASE_TYPE(e) ((base_type)e)
#define BASE_PT(e) ((base_pt)e)
#define BASE_TYPE_SIZE (ALIGN(sizeof(base_type)))

// 堆头部初始化标志
#define INITED ~0

// 块头部的值的下标  相对块的内容的第一个字节的指针
#define OVHD_PREV_IDX -3
#define OVHD_NEXT_IDX -2
#define OVHD_SIZE_IDX -1
// 堆头部得下标
#define HD_INIT_IDX 0
#define FB_LIST_NUM 9
// 堆头部的值的个数v
#define HD_N (FB_LIST_NUM + 1)
// 块头部的值的个数  OV为Overhead
#define OVHD_N 3
// 堆头部的大小
#define HD_SIZE (BASE_TYPE_SIZE * HD_N)
// 块头部的大小
#define OVHD_SIZE (BASE_TYPE_SIZE * OVHD_N)
// 块尾部的大小
#define TAIL_SIZE BASE_TYPE_SIZE
// 一个块头部加尾部的大小
#define OV_SIZE (ALIGN(OVHD_SIZE + TAIL_SIZE))

// 将一个指针前进n个字节,n为负数则为后退
#define inc_n_byte(p, n) ((base_pt)((((char*)(p)) + n)))
// 堆上非堆头部的最小的地址
#define HEAP_START_PT (inc_n_byte(mem_heap_lo(), HD_SIZE))

// 给定块内容的大小,计算整个块最小的size
#define calc_blcok_total_size(alloc_size) (OVHD_SIZE + ALIGN(alloc_size) + TAIL_SIZE)

// 用于块的标志位 0为已分配 1为空闲块
#define BLOCK_USED 0
#define BLOCK_FREE 1
#define BLOCK_FLAG_BITS 1
#define BLOCK_FLAG_MASK ((1 << BLOCK_FLAG_BITS) - 1)

// 用size和flag构造出存储在块size部分的值
#define calc_size_val(size, flag) ((size << BLOCK_FLAG_BITS) | flag)

// 将指针p看成base_pt类型 读取p[idx] 和 写入 p[idx] = v
#define m_read(p, idx) (((base_pt)(p))[((base_type)(idx))])
#define m_write(p, idx, v) (m_read(p, idx) = ((base_type)v))

// 读写堆头部的数据结构
#define hd_read(idx) (m_read(mem_heap_lo(), idx))
#define hd_write(idx, v) (m_write(mem_heap_lo(), idx, v))

// 读取堆头部是否初始化的信息
#define hd_inited (m_read(mem_heap_lo(), HD_INIT_IDX))

// 设置堆头部已经初始化
#define set_hd_inited() (m_write(mem_heap_lo(), HD_INIT_IDX, INITED))

// p为看成指向一个块的内容部分的第一个字节的指针,读取这个块的标志位,大小,next指针,prev指针,整个块的最后一个字节
#define block_flag(p) ((m_read(p, OVHD_SIZE_IDX)) & BLOCK_FLAG_MASK)
#define block_size(p) ((m_read(p, OVHD_SIZE_IDX)) >> BLOCK_FLAG_BITS)
#define block_next(p) (BASE_PT(m_read(p, OVHD_NEXT_IDX)))
#define block_prev(p) (BASE_PT(m_read(p, OVHD_PREV_IDX)))
#define block_last(p) (inc_n_byte(p, ((block_size(p)) - (OV_SIZE))))
// p为指向一个块的第一个字节的指针,计算得到指向这个块的内容部分的第一个字节的指针
#define block_content_pt(p) (inc_n_byte(p, OVHD_SIZE))

// v为块的size1部分的内容,看是否为空闲块
#define is_free(v)  (((v) & BLOCK_FLAG_BITS) != BLOCK_USED)
// p为看成指向一个块的内容部分的第一个字节的指针,看这个块是空闲块还是已分配块
#define is_block_used(p) ((block_flag(p)) == (BLOCK_USED))
#define is_block_free(p) ((block_flag(p)) != (BLOCK_USED))

// p为看成指向一个块的内容部分的第一个字节的指针,设置头部的size部分,尾部的size部分,next指针,prev指针
#define set_block_hd_size(p, v) (m_write(p, OVHD_SIZE_IDX, v))
#define set_block_td_size(p, v) (m_write(block_last(p), 0, v))
#define set_block_next(p, v) (m_write(p, OVHD_NEXT_IDX, v))
#define set_block_prev(p, v) (m_write(p, OVHD_PREV_IDX, v))

// 用于在分裂空闲块时,如剩下部分小于这个值,就不再拆开块
#define REMIAN_SIZE (ALIGN(sizeof(base_type) * 3))
#define EXTRA_SAPCE (ALIGN(REMIAN_SIZE + OV_SIZE))

// 用于调试 打印各种type的大小
#define print(m_type) (printf("sizeof(" #m_type "): %d\n", sizeof(m_type)))

// 是否是调试模式
#define DEBUG_MODE 0

// 给定一个指向一个块的内容部分的第一个字节的指针,设置其size部分的值,包括头部和尾部
void set_block_size(base_pt block, base_type total_size, base_type block_flag) {
    base_type size_val = calc_size_val(total_size, block_flag);
    set_block_hd_size(block, size_val);
    set_block_td_size(block, size_val);
}

// 同上,只是不用再填 block_flag
#define set_block_used(block, total_size) (set_block_size(block, total_size, BLOCK_USED))
#define set_block_free(block, total_size) (set_block_size(block, total_size, BLOCK_FREE))

// 功能同上 只修改flag 不改变块大小
#define mark_block_used(block)  (set_block_used(block, block_size(block)))
#define mark_block_free(block)  (set_block_free(block, block_size(block)))


void px(void* p, int x) {
    for (int i = 0; i < x; i++) {
        printf("i:%d, addr: 0x%x, content:0x%x\n", i, &m_read(p, i), m_read(p, i));
    }  
}

void print_type_info() {
    printf("\n");
    print(char*);
    print(void*);
    print(base_pt);
    print(base_type);
    print(int);
    printf("\n");
}

// 给定整个块的大小,返回其应该在的链表头指针在堆头部的下标
base_type get_list_idx(base_type total_size) {
    base_type idx[FB_LIST_NUM - 1] = {48, 64, 128, 256, 512, 1024, 2072, 4104};
    base_type i = 0;
    for (; i < FB_LIST_NUM - 1; i++)
        if (total_size <= idx[i])
            break;
    return i + 1;
}

// 将一个块插入空闲链表的头部
void insert_into_free_list_head(base_pt block) {
    base_type size = block_size(block);
    base_type list_idx = get_list_idx(size);
    // if (DEBUG_MODE)
    //     printf("insert block(addr:%p size:%d) into list %d\n", block, size, list_idx);
    set_block_free(block, size);       
    set_block_prev(block, NULL);
    set_block_next(block, NULL);
    base_pt head = BASE_PT(hd_read(list_idx));
    if (head == NULL) {
        hd_write(list_idx, block);
    }
    else {
        base_pt old_head = head;
        hd_write(list_idx, block);
        set_block_next(block, old_head);
        if (old_head)
            set_block_prev(old_head, block);
    }
}

// 调试用的函数  用于打印出所有的空闲链表
void print_list() {
    // printf("\n");
    for (base_type i = 1; i <= FB_LIST_NUM; i++) {
        printf("list: idx=%d, element:", i);
        base_pt pt = hd_read(i);
        while(pt != NULL) {
            printf("[%p(%d:%d)]", pt, block_size(pt), block_flag(pt));
            pt = block_next(pt);
            if (pt)
                printf(" -> ");
        }
        printf("\n");
    }
    printf("\n");
}

// 在所有空闲链表中查找>=total_size的空闲块,返回块指针
base_pt search_free_list(base_type total_size) {
    base_type list_idx = get_list_idx(total_size);
    // 先计算出起始要找的链表的下标,这个链表如果没找到,就再到下一个链表继续找
    for (base_type idx = list_idx; idx <= FB_LIST_NUM; idx++) {
        base_pt pt = BASE_PT(hd_read(idx));
        while (pt && (block_size(pt) < total_size))
            pt = block_next(pt);
        if (pt) {
            if (DEBUG_MODE)
                printf("found free block(addr:%p, size:%d)\n", pt, block_size(pt));
            return pt;
        }
    }
    if (DEBUG_MODE)
        printf("found no free block\n");
    return NULL;
}

// 将一个块从空闲链表中移除
void remove_from_free_list(base_pt block) {
    base_type list_idx = get_list_idx(block_size(block));
    if (!block_prev(block))
        hd_write(list_idx, block_next(block));
    else
        set_block_next(block_prev(block), block_next(block));
        
    if (block_next(block))
        set_block_prev(block_next(block), block_prev(block));

    set_block_prev(block, NULL);
    set_block_next(block, NULL);
    mark_block_used(block);
    return block;
}

// 空闲块太大,将一个空闲块进行分裂
base_pt split_block(base_pt block, base_type total_size) {
    base_type size = block_size(block);
    if (is_block_used(block)) {
        printf("split block error: try to split an used block\n");
        return NULL;
    }

    if (size < total_size) {
        printf("split block error: try to split block into bigger blocks.\n");
        return NULL;
    }
    else {
        remove_from_free_list(block);
        // 剩下部分比较多,就把剩下部分变成一个新的空闲块插入链表
        // 剩下部分不多就把整个块都分配出去
        if (size >= (total_size + EXTRA_SAPCE)) {
            base_pt new_block = inc_n_byte(block, total_size);
            
            set_block_used(block, total_size);
            set_block_used(new_block, size - total_size);

            if (DEBUG_MODE)
                printf("split free block(addr:%p, size:%d) into ret block(addr:%p, size:%d) and ", block, size, block, block_size(block));
            
            if (DEBUG_MODE)
                printf("new block(addr:%p, size:%d)\n", new_block, block_size(new_block));
            insert_into_free_list_head(new_block);
        }
        
        return block;
    }
}

// 真正向系统要求分配一个大小为total_size的块
void* true_malloc(base_type total_size) {
    base_pt *p = BASE_PT(mem_sbrk(total_size));
    // printf("info: true malloc has got a new block with addr: %p, size: %llu\n", p,  total_size);
    if (p == -1)
        return NULL;
    p = block_content_pt(p);
    set_block_next(p, NULL);
    set_block_prev(p, NULL);
    set_block_used(p, total_size);

    return (void*)p;
}

// 将一个块与其前后的空闲块进行合并
base_pt merge_prev_next_block(base_pt ptr) {
    base_pt pt = BASE_PT(ptr);
    base_type size = block_size(pt);
    if (DEBUG_MODE)
        printf("-----------------free analysis for block(addr:%p, size:%d)------------------------\n", pt, size);
    // 指向前一个块的尾部的size部分的指针
    base_type prev_block_size_val_pt = inc_n_byte(pt, -(OVHD_SIZE + BASE_TYPE_SIZE));
    if (DEBUG_MODE)
        printf("prev: size_val_pt:%p heap_start:%p\n", prev_block_size_val_pt, HEAP_START_PT);
    // 指向前一个块的尾部的size部分的指针 不能小于 堆真正开始处的地址
    if (prev_block_size_val_pt > HEAP_START_PT) {
        // 解析处前一个块的size部分的值 由size和flag构成
        base_type prev_block_size_val = m_read(pt, -(OVHD_N + 1));
        if (DEBUG_MODE)
            printf("prev_block_size_val:0x%x, flag:%x\n", prev_block_size_val, prev_block_size_val & BLOCK_FLAG_MASK);
        // 如果前一个块为空闲块
        if (is_free(prev_block_size_val)) {
            base_type prev_block_size = (prev_block_size_val >> BLOCK_FLAG_BITS);
            base_pt prev_block = inc_n_byte(pt, -prev_block_size);
            if (DEBUG_MODE)
                printf("merge current block(addr:%p size:%d) and prev block(addr:%p size:%d) into ", pt, size, prev_block, prev_block_size);
            remove_from_free_list(prev_block);
            size += prev_block_size;
            pt = prev_block;
            set_block_size(pt, size, BLOCK_USED);
            if (DEBUG_MODE)
                printf("new block(addr:%p, size:%d)\n", pt, block_size(pt));
        }
    }

    // 指向下一个块的内容部分的第一个字节的指针
    base_pt next_block = inc_n_byte(pt, size);
    if (DEBUG_MODE)
        printf("next: block_pt:%p heap_hi:%p\n", next_block, mem_heap_hi());
    // 指向下一个块的内容部分的第一个字节的指针 不能超出堆的最大范围
    if (next_block < mem_heap_hi()) {
        // 找出下一个块的size部分的值 包括size和flag
        base_type next_block_size_val = m_read(next_block, OVHD_SIZE_IDX);
        if (DEBUG_MODE)
            printf("next_block_size_val:0x%x flag:%x\n", next_block_size_val, next_block_size_val & BLOCK_FLAG_MASK);
        // 如果为空闲块
        if (is_free(next_block_size_val)) {
            // 下一个块的大小
            base_type next_block_size = (next_block_size_val >> BLOCK_FLAG_BITS);
            if (DEBUG_MODE)
                printf("merge current block(addr:%p size:%d) and next block(addr:%p size:%d) into", pt, size, next_block, next_block_size);
            remove_from_free_list(next_block);
            set_block_size(pt, size + next_block_size, BLOCK_USED);
            if (DEBUG_MODE)
                printf("new block(addr:%p, size:%d)\n", pt, block_size(pt));
        }
    }
    if (DEBUG_MODE)
        printf("----------------------------------free analysis end---------------------------------------------\n");

    return pt; 
}

int mm_init (void) {
    // print_type_info();
    base_type r = BASE_TYPE(mem_sbrk(HD_SIZE));
    if (r == -1)
        return -1;
    set_hd_inited();
    // 将各个链表的指针设置为NULL
    for (base_type i = 1; i <= FB_LIST_NUM; i++)
        hd_write(i, NULL);
    // printf("low: 0x%x, high:0x%x\n", mem_heap_lo(), mem_heap_hi());
    // px(mem_heap_lo(), 5);
    return 0;
}

void * mm_malloc (size_t size) {
    if (!size)  return NULL;

    if (hd_inited != INITED) {
        printf("error: try to call malloc before inited.\n");
        return NULL;
    }
    base_type total_size = calc_blcok_total_size(size);
    base_pt pt = search_free_list(total_size);
    if (pt == NULL) {
        //  请参考merge_prev_next_block
        base_type alloc_size = total_size;
        base_pt prev_block_size_pt = inc_n_byte(mem_heap_hi(), -(BASE_TYPE_SIZE - 1));
        if (DEBUG_MODE) {
            base_type size_val = *prev_block_size_pt;
            printf("prev_block_size_pt:%p heap_start:%p, size:%d, flag:%d\n", prev_block_size_pt, 
                HEAP_START_PT, size_val >> BLOCK_FLAG_BITS, size_val & BLOCK_FLAG_MASK);
        }
        
        if (prev_block_size_pt > HEAP_START_PT & is_free((*prev_block_size_pt))) {
            // 在没有适合的空闲块时,要分配一个,如前面一个块为空闲块,则可以减少分配量
            base_type prev_block_size = ((*prev_block_size_pt) >> BLOCK_FLAG_BITS);
            base_pt new_block = true_malloc(total_size - prev_block_size + OV_SIZE);
            if (DEBUG_MODE) {
                printf("malloc: prev block(addr:%p size:%d flag:%d)\n", 0, prev_block_size, ((*prev_block_size_pt) & BLOCK_FLAG_MASK));
                printf("true malloc: block(addr:%p size:%d flag:%d)\n", new_block, block_size(new_block), block_flag(new_block));
            }
            base_pt merged_pt = merge_prev_next_block(new_block);
            if (DEBUG_MODE) {
                printf("malloc: request size: %d, allocated addr: %p, size: %d\n", size, merged_pt, block_size(merged_pt));
                print_list();
            }
            return merged_pt;
        }
        // 直接向系统申请分配
        void *r =  true_malloc(total_size);
        if (DEBUG_MODE) {
            printf("malloc: request size: %d, allocated addr: %p, size: %d\n", size, r, block_size(r));
            print_list();
        }
        return r;
    }
    else {
        // 存在所需的空闲块,对其进行分裂
        void *r = split_block(pt, total_size);
        if (DEBUG_MODE) {
            printf("malloc: request size: %d, allocated addr: %p, size: %d\n", size, r, block_size(r));
            print_list();
        }
        return r;
    }
}

void mm_free(void *ptr) {
    if (hd_inited != INITED) {
        printf("free rror: try to call free before inited.\n");
        return;
    }

    if (is_block_free(ptr)) {
        printf("free error: try to free a free block with addr: %p\n", ptr);
        return;
    }
    if (DEBUG_MODE)
        printf("free: try to free a block with addr:%p, size:%d\n", ptr, block_size(ptr));
    
    // 先合并前后的空闲块,再插入空闲块链表
    base_pt pt = merge_prev_next_block(ptr);
    insert_into_free_list_head(pt);

    if (DEBUG_MODE)
        print_list();
}

void *mm_realloc(void *ptr, size_t size) {
    if (hd_inited != INITED) {
        printf("error: try to call realloc before inited.\n");
        return NULL;
    }
    if (DEBUG_MODE)
        printf("realloc: try to realloc a block with addr:%p, size:%d\n", ptr, size);
    if (!ptr)   return mm_malloc(size);
    if (!size)  {
        mm_free(ptr);
        return NULL;
    }
    base_type blockSize = block_size(ptr);
    base_type needed_total_size = calc_blcok_total_size(size);
    if (needed_total_size <= blockSize) {
        if (DEBUG_MODE)
            print_list();
        return ptr;
    }
    
    // 当前后可能有空闲块时,可以合并后再看能否满足用户的需要
    else {
        
        // 下面这种实现不对,在mm_malloc中如果调用了split_block,那么会修改块尾部的长度值,可能篡改原来block数据
        // mm_free(ptr);
        // base_pt new_ptr = mm_malloc(size);
        // if (!new_ptr)   return NULL;
        // if (DEBUG_MODE)
        //     printf("realloc: origin block(addr:%p, size:%d), new block(addr:%p, size:%d)\n", ptr, block_size(ptr), new_ptr, block_size(new_ptr));
        // memcpy(new_ptr, ptr, blockSize - OV_SIZE);
        // if (DEBUG_MODE) {
        //     printf("copy from old block to new block finished, total copy size:%d!\n", blockSize - OV_SIZE);
        //     print_list();
        // }
        // return new_ptr;

        // 正确的实现
        // 先将前后的块合并
        base_pt merged_pt = merge_prev_next_block(ptr);
        base_type merged_block_size = block_size(merged_pt);
        base_pt final_block;
        // 合并块大小不够用就直接malloc
        if (merged_block_size < needed_total_size) {
            insert_into_free_list_head(merged_pt);
            final_block = mm_malloc(size);
            memcpy(final_block, ptr, blockSize - OV_SIZE);
            return final_block;
        }
        // 合并块够大就需要拆分
        else if (merged_block_size >= (needed_total_size + EXTRA_SAPCE)) {
            final_block = merged_pt;
            // 必须先将块的内容复制过去,再拷贝
            memcpy(final_block, ptr, blockSize - OV_SIZE);

            base_pt new_block = inc_n_byte(final_block, needed_total_size);
            set_block_used(final_block, needed_total_size);
            set_block_free(new_block, merged_block_size - needed_total_size);

            if (DEBUG_MODE)
                printf("split free block(addr:%p, size:%d) into ret block(addr:%p, size:%d) and ", merged_pt, merged_block_size, merged_pt, block_size(merged_pt));
            
            if (DEBUG_MODE)
                printf("new block(addr:%p, size:%d)\n", new_block, block_size(new_block));
            insert_into_free_list_head(new_block);
            return final_block;
        }
        // 块够用但不够拆分
        else {
            final_block = merged_pt;
            memcpy(final_block, ptr, blockSize - OV_SIZE);
            return final_block;
        }
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值