csapp读书笔记-实现malloc动态存储

来源:互联网 发布:linux top c 命令详解 编辑:程序博客网 时间:2024/05/22 00:49
1.分配器的要求和目标
显式分配器必须在一些相当严格的约束条件下工作:
处理任意请求序列。
立即响应请求。 
不允许为提高性能重新排列或缓冲请求。
只使用堆。
对齐块(对齐要求)。 
不修改已分配的块。
在这些限制条件下,分配器的编写者试图实现吞吐率最大化和存储器使用率最大化,而这两个性能目标通常是相互冲突的。
目标1:最大化吞吐率
假定n个分配和释放请求的某种序列R0,R1,R2.....Rn-1,我们希望一个分配器的吞吐率最大化,吞吐率定义为每个单位时间里完成的请求数。一般而言,我们可以通过使满足分配和释放请求的平均时间最小化来使吞吐率最大化。
目标2:最大化存储器利用率
最有效的标准是峰值利用率(peak utilization)。假定n个分配和释放请求的某种序列R0,R1,R2.....Rk……Rn-1,如果一个应用程序请求一个p字节的块,那么得到的已分配块的有效载荷是p字节。聚集有效载荷(aggregate payload)表示为Pk,为当前已分配的块的有效载荷之和,而Hk表示堆的当前的(单调非递减的)大小。
那么前k+1个请求的峰值利用率表示为Uk,可以通过下式得到:


吞吐率和存储器利用率是相互牵制的,分配器设计的一个有趣的挑战就是在两者之间找到一个平衡。
2.碎片
造成堆利用率很低的主要原因是一种称为碎片(fragmentation)的现象。
内部碎片:已分配块比有效载荷大时发生。内部碎片的数量只取决于以前请求的模式和分配器的实现方式。 
外部碎片:当空闲存储器合计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以处理这个请求发生的。外部碎片比内部碎片的量化要困难的多,因为它不仅取决于以前请求的模式和分配器的实现方式,还取决于将来请求的模式。分配器通常采用启发式策略维持少量的大空闲块,而不是大量的小空闲块。
3.实现问题
空闲块组织:如何记录空闲块
放置:如何选择一个合适的空闲快来放置一个新分配的块
分割:将一个新分配的块放入某个空闲块后,如何处理这个空闲快中的剩余部分
合并:我们如何处理一个刚刚被释放的块
4.隐式空闲链表

一个块由一个字的头部,有效载荷,以及可能的一些额外的填充组成。 

我们称这种结构为隐式空闲链表,是因为空闲块是通过头部中的大小字段隐含地连接着的。分配器可以通过遍历堆中所有的块,从而间接地遍历整个空闲块的集合。隐式空闲链表的优点是简单。显著的缺点是任何操作的开销,例如放置分配的块,要求空闲链表的搜索与堆中已分配块和空闲块的总数呈线性关系。
5.放置已分配的块
首次适配:从头开始搜索空闲链表,选择第一个合适的空闲块。
下一次适配:和首次适配很类似,但不是从头开始,而是从上一次查询的地方开始。
最佳适配 :检查每个空闲块,找一个满足条件的最小的空闲块。
优缺点
6.分割空闲块
用整个空闲块:产生更多的内部碎片。
分割空闲块:第一部分变成分配块,而剩下的变成一个新的空闲块。

7.获取额外的堆存储器
如果最大化合并还是不能生成一个足够大的块,或者空闲块已经最大程度地合并了,那么分配器就会通过调用sbrk函数,向内核请求额外的堆存储器。分配器将额外的存储器转化成一个大的空闲块,将这个块插入到空闲链表中,然后将被请求的块放置在这个新的空闲块中。
8.合并空闲块


立即合并:块被释放时,合并所有相邻的块。
推迟合并:等到某个稍晚的时候再合并空闲块,比如某个分配请求失败的时候。
在对分配器的讨论中,我们会假设使用立即合并。但是你应该了解,快速的分配器通常会选择某种形式的推迟合并。
9.带边界标记的合并
Knuth提出边界标记,允许在常数时间内进行对前面块的合并。在每个块的结尾处添加一个脚部,其中脚部就是头部的一个副本。



10.综合:实现一个简单的分配器
memlib.c

#include <stdio.h>#include <stdlib.h>#include "csapp.h"#define MAX_HEAP (1 << 30)static char *mem_heap;     /* Points to first byte of heap */ static char *mem_brk;      /* Points to last byte of heap plus 1 */static char *mem_max_addr; /* Max legal heap addr plus 1*/ void mem_init(void){    mem_heap = (char *)Malloc(MAX_HEAP);    mem_brk = (char *)mem_heap;                   mem_max_addr = (char *)(mem_heap + MAX_HEAP); }/*  * mem_sbrk - Simple model of the sbrk function. Extends the heap  *    by incr bytes and returns the start address of the new area. In *    this model, the heap cannot be shrunk. */void *mem_sbrk(int incr) {    char *old_brk = mem_brk;    if ( (incr < 0) || ((mem_brk + incr) > mem_max_addr)) {errno = ENOMEM;fprintf(stderr, "ERROR: mem_sbrk failed. Ran out of memory...\n");return (void *)-1;    }    mem_brk += incr;    return (void *)old_brk;}
mm.c
/*  * Simple, 32-bit and 64-bit clean allocator based on implicit free * lists, first fit placement, and boundary tag coalescing, as described * in the CS:APP2e text. Blocks must be aligned to doubleword (8 byte)  * boundaries. Minimum block size is 16 bytes.  */#include <stdio.h>#include <string.h>#include <stdlib.h>#include "mm.h"#include "memlib.h"/* * If NEXT_FIT defined use next fit search, else use first fit search  */#define NEXT_FITx/* $begin mallocmacros *//* Basic constants and macros */#define WSIZE       4       /* Word and header/footer size (bytes) */ //line:vm:mm:beginconst#define DSIZE       8       /* Doubleword size (bytes) */#define CHUNKSIZE  (1<<12)  /* Extend heap by this amount (bytes) */  //line:vm:mm:endconst #define MAX(x, y) ((x) > (y)? (x) : (y))  /* Pack a size and allocated bit into a word */#define PACK(size, alloc)  ((size) | (alloc)) //line:vm:mm:pack/* Read and write a word at address p */#define GET(p)       (*(unsigned int *)(p))            //line:vm:mm:get#define PUT(p, val)  (*(unsigned int *)(p) = (val))    //line:vm:mm:put/* Read the size and allocated fields from address p */#define GET_SIZE(p)  (GET(p) & ~0x7)                   //line:vm:mm:getsize#define GET_ALLOC(p) (GET(p) & 0x1)                    //line:vm:mm:getalloc/* Given block ptr bp, compute address of its header and footer */#define HDRP(bp)       ((char *)(bp) - WSIZE)                      //line:vm:mm:hdrp#define FTRP(bp)       ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) //line:vm:mm:ftrp/* Given block ptr bp, compute address of next and previous blocks */#define NEXT_BLKP(bp)  ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE))) //line:vm:mm:nextblkp#define PREV_BLKP(bp)  ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) //line:vm:mm:prevblkp/* $end mallocmacros *//* Global variables */static char *heap_listp = 0;  /* Pointer to first block */  #ifdef NEXT_FITstatic char *rover;           /* Next fit rover */#endif/* Function prototypes for internal helper routines */static void *extend_heap(size_t words);static void place(void *bp, size_t asize);static void *find_fit(size_t asize);static void *coalesce(void *bp);static void printblock(void *bp); static void checkheap(int verbose);static void checkblock(void *bp);/*  * mm_init - Initialize the memory manager  *//* $begin mminit */int mm_init(void) {    /* Create the initial empty heap */    if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1) //line:vm:mm:begininitreturn -1;    PUT(heap_listp, 0);                          /* Alignment padding */    PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 1)); /* Prologue header */     PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 1)); /* Prologue footer */     PUT(heap_listp + (3*WSIZE), PACK(0, 1));     /* Epilogue header */    heap_listp += (2*WSIZE);                     //line:vm:mm:endinit  /* $end mminit */#ifdef NEXT_FIT    rover = heap_listp;#endif/* $begin mminit */    /* Extend the empty heap with a free block of CHUNKSIZE bytes */    if (extend_heap(CHUNKSIZE/WSIZE) == NULL) return -1;    return 0;}/* $end mminit *//*  * mm_malloc - Allocate a block with at least size bytes of payload  *//* $begin mmmalloc */void *mm_malloc(size_t size) {    size_t asize;      /* Adjusted block size */    size_t extendsize; /* Amount to extend heap if no fit */    char *bp;      /* $end mmmalloc */    if (heap_listp == 0){mm_init();    }/* $begin mmmalloc */    /* Ignore spurious requests */    if (size == 0)return NULL;    /* Adjust block size to include overhead and alignment reqs. */    if (size <= DSIZE)                                          //line:vm:mm:sizeadjust1asize = 2*DSIZE;                                        //line:vm:mm:sizeadjust2    elseasize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE); //line:vm:mm:sizeadjust3    /* Search the free list for a fit */    if ((bp = find_fit(asize)) != NULL) {  //line:vm:mm:findfitcallplace(bp, asize);                  //line:vm:mm:findfitplacereturn bp;    }    /* No fit found. Get more memory and place the block */    extendsize = MAX(asize,CHUNKSIZE);                 //line:vm:mm:growheap1    if ((bp = extend_heap(extendsize/WSIZE)) == NULL)  return NULL;                                  //line:vm:mm:growheap2    place(bp, asize);                                 //line:vm:mm:growheap3    return bp;} /* $end mmmalloc *//*  * mm_free - Free a block  *//* $begin mmfree */void mm_free(void *bp){/* $end mmfree */    if(bp == 0) return;/* $begin mmfree */    size_t size = GET_SIZE(HDRP(bp));/* $end mmfree */    if (heap_listp == 0){mm_init();    }/* $begin mmfree */    PUT(HDRP(bp), PACK(size, 0));    PUT(FTRP(bp), PACK(size, 0));    coalesce(bp);}/* $end mmfree *//* * coalesce - Boundary tag coalescing. Return ptr to coalesced block *//* $begin mmfree */static void *coalesce(void *bp) {    size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp)));    size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));    size_t size = GET_SIZE(HDRP(bp));    if (prev_alloc && next_alloc) {            /* Case 1 */return bp;    }    else if (prev_alloc && !next_alloc) {      /* Case 2 */size += GET_SIZE(HDRP(NEXT_BLKP(bp)));PUT(HDRP(bp), PACK(size, 0));PUT(FTRP(bp), PACK(size,0));    }    else if (!prev_alloc && next_alloc) {      /* Case 3 */size += GET_SIZE(HDRP(PREV_BLKP(bp)));PUT(FTRP(bp), PACK(size, 0));PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));bp = PREV_BLKP(bp);    }    else {                                     /* Case 4 */size += GET_SIZE(HDRP(PREV_BLKP(bp))) +     GET_SIZE(FTRP(NEXT_BLKP(bp)));PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));bp = PREV_BLKP(bp);    }/* $end mmfree */#ifdef NEXT_FIT    /* Make sure the rover isn't pointing into the free block */    /* that we just coalesced */    if ((rover > (char *)bp) && (rover < NEXT_BLKP(bp))) rover = bp;#endif/* $begin mmfree */    return bp;}/* $end mmfree *//* * mm_realloc - Naive implementation of realloc */void *mm_realloc(void *ptr, size_t size){    size_t oldsize;    void *newptr;    /* If size == 0 then this is just free, and we return NULL. */    if(size == 0) {mm_free(ptr);return 0;    }    /* If oldptr is NULL, then this is just malloc. */    if(ptr == NULL) {return mm_malloc(size);    }    newptr = mm_malloc(size);    /* If realloc() fails the original block is left untouched  */    if(!newptr) {return 0;    }    /* Copy the old data. */    oldsize = GET_SIZE(HDRP(ptr));    if(size < oldsize) oldsize = size;    memcpy(newptr, ptr, oldsize);    /* Free the old block. */    mm_free(ptr);    return newptr;}/*  * checkheap - We don't check anything right now.  */void mm_checkheap(int verbose)  { }/*  * The remaining routines are internal helper routines  *//*  * extend_heap - Extend heap with free block and return its block pointer *//* $begin mmextendheap */static void *extend_heap(size_t words) {    char *bp;    size_t size;    /* Allocate an even number of words to maintain alignment */    size = (words % 2) ? (words+1) * WSIZE : words * WSIZE; //line:vm:mm:beginextend    if ((long)(bp = mem_sbrk(size)) == -1)  return NULL;                                        //line:vm:mm:endextend    /* Initialize free block header/footer and the epilogue header */    PUT(HDRP(bp), PACK(size, 0));         /* Free block header */   //line:vm:mm:freeblockhdr    PUT(FTRP(bp), PACK(size, 0));         /* Free block footer */   //line:vm:mm:freeblockftr    PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); /* New epilogue header */ //line:vm:mm:newepihdr    /* Coalesce if the previous block was free */    return coalesce(bp);                                          //line:vm:mm:returnblock}/* $end mmextendheap *//*  * place - Place block of asize bytes at start of free block bp  *         and split if remainder would be at least minimum block size *//* $begin mmplace *//* $begin mmplace-proto */static void place(void *bp, size_t asize)     /* $end mmplace-proto */{    size_t csize = GET_SIZE(HDRP(bp));       if ((csize - asize) >= (2*DSIZE)) { PUT(HDRP(bp), PACK(asize, 1));PUT(FTRP(bp), PACK(asize, 1));bp = NEXT_BLKP(bp);PUT(HDRP(bp), PACK(csize-asize, 0));PUT(FTRP(bp), PACK(csize-asize, 0));    }    else { PUT(HDRP(bp), PACK(csize, 1));PUT(FTRP(bp), PACK(csize, 1));    }}/* $end mmplace *//*  * find_fit - Find a fit for a block with asize bytes  *//* $begin mmfirstfit *//* $begin mmfirstfit-proto */static void *find_fit(size_t asize)/* $end mmfirstfit-proto */{/* $end mmfirstfit */#ifdef NEXT_FIT     /* Next fit search */    char *oldrover = rover;    /* Search from the rover to the end of list */    for ( ; GET_SIZE(HDRP(rover)) > 0; rover = NEXT_BLKP(rover))if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover))))    return rover;    /* search from start of list to old rover */    for (rover = heap_listp; rover < oldrover; rover = NEXT_BLKP(rover))if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover))))    return rover;    return NULL;  /* no fit found */#else /* $begin mmfirstfit */    /* First fit search */    void *bp;    for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) {if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp)))) {    return bp;}    }    return NULL; /* No fit *//* $end mmfirstfit */#endif}static void printblock(void *bp) {    size_t hsize, halloc, fsize, falloc;    checkheap(0);    hsize = GET_SIZE(HDRP(bp));    halloc = GET_ALLOC(HDRP(bp));      fsize = GET_SIZE(FTRP(bp));    falloc = GET_ALLOC(FTRP(bp));      if (hsize == 0) {printf("%p: EOL\n", bp);return;    }    /*  printf("%p: header: [%p:%c] footer: [%p:%c]\n", bp, hsize, (halloc ? 'a' : 'f'), fsize, (falloc ? 'a' : 'f')); */}static void checkblock(void *bp) {    if ((size_t)bp % 8)printf("Error: %p is not doubleword aligned\n", bp);    if (GET(HDRP(bp)) != GET(FTRP(bp)))printf("Error: header does not match footer\n");}/*  * checkheap - Minimal check of the heap for consistency  */void checkheap(int verbose) {    char *bp = heap_listp;    if (verbose)printf("Heap (%p):\n", heap_listp);    if ((GET_SIZE(HDRP(heap_listp)) != DSIZE) || !GET_ALLOC(HDRP(heap_listp)))printf("Bad prologue header\n");    checkblock(heap_listp);    for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) {if (verbose)     printblock(bp);checkblock(bp);    }    if (verbose)printblock(bp);    if ((GET_SIZE(HDRP(bp)) != 0) || !(GET_ALLOC(HDRP(bp))))printf("Bad epilogue header\n");}
config.h
#ifndef __CONFIG_H_#define __CONFIG_H_/* * config.h - malloc lab configuration file * * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. * May not be used, modified, or copied without permission. *//* * Config variables for submitting autoresults to the Autolab server * Note: The COURSE and LAB variables must match the directory names * on the Autolab server. */#define SERVERHOST "char.cmcl.cs"  /* Autolab server domain name */#define SERVERPORT 15213           /* Autolab server well-known port */#define COURSE "15213-f06"         /* Course name */#define LAB "malloclab"           /* Lab name *//* * This is the default path where the driver will look for the * default tracefiles. You can override it at runtime with the -t flag. */#define TRACEDIR "./traces/"/* * This is the list of default tracefiles in TRACEDIR that the driver * will use for testing. Modify this if you want to add or delete * traces from the driver's test suite. * * The first four test correctness.  The last several test utilization * and performance. */#define DEFAULT_TRACEFILES \"corners.rep", \"short2.rep", \"malloc.rep", \"binary-bal.rep", \"coalescing-bal.rep", \"fs.rep", \"hostname.rep", \"login.rep", \"ls.rep", \"perl.rep", \"random-bal.rep", \"rm.rep", \"xterm.rep"/*  * Students can get more points for building faster allocators, up to * this point (in ops / sec) */#define MAX_SPEED       15000E3/*  * Students can get more points for building more efficient allocators, * up to this point (1 is perfect). */#define MAX_SPACE       0.93 /*  * This constant determines the contributions of space utilization  * (UTIL_WEIGHT) and throughput (1 - UTIL_WEIGHT) to the performance  * index.  */#define UTIL_WEIGHT .60/* * Alignment requirement in bytes (either 4 or 8) */#define ALIGNMENT 8/* * Maximum heap size in bytes */#define MAX_HEAP (20*(1<<20))  /* 20 MB *//***************************************************************************** * Set exactly one of these USE_xxx constants to "1" to select a timing method *****************************************************************************/#define USE_FCYC   1   /* cycle counter w/K-best scheme (x86 & Alpha only) */#define USE_ITIMER 0   /* interval timer (any Unix box) */#define USE_GETTOD 0   /* gettimeofday (any Unix box) */#endif /* __CONFIG_H */
memlib.h
#include <unistd.h>void mem_init(void);               void *mem_sbrk(int incr);void mem_deinit(void);void mem_reset_brk(void); void *mem_heap_lo(void);void *mem_heap_hi(void);size_t mem_heapsize(void);size_t mem_pagesize(void);
mm.h
/* $begin mallocinterface */extern int mm_init(void);extern void *mm_malloc (size_t size);extern void mm_free (void *ptr);/* $end mallocinterface */extern void *mm_realloc(void *ptr, size_t size);extern void *mm_calloc (size_t nmemb, size_t size);extern void mm_checkheap(int verbose);






0 0
原创粉丝点击