malloc的实现

  在做csapp的malloc实验,一开始是按照书上的隐式链表法,发现得分很低。这种方法确实很挫,需要遍历一遍以找到合适的空闲块。于是我想到《STL源码剖析》中stl的内存池,感觉应该可以用类似的方法做,因为malloc要做的事情实际就是为了防止内存碎片和减少系统调用,实际就是一个内存池。但是书上介绍的stl的内存池是没有边界区块的,也就是没办法合并空闲区块,这样可能产生过多的外部碎片。所以我最终的做法是大体上采用stl的方法,再加上边界区块,以方便合并空闲区块。

  首先是根据需要的大小在freelist中找到合适的位置

int getListOffset(size_t size)  {
    size_t n = -4;
     while((size = size>>1))
        ++n;
    if(n>16)
        return 16;
    if(n<0)
        return 0;
    return n;
}

freelist数组在初始化时创建,将指向空闲块的指针插入freelist的操作如下

/*
 * insert_list - 将free block插入到相应大小的free list中, 插入位置为表头
 */
void insert_list(void *bp)
{
    int index;
    size_t size;
    size = GET_SIZE(HDRP(bp));
    index = getListOffset(size);

    if (GET_PTR(heap_listp + WSIZE * index) == NULL) {
        PUT_PTR(heap_listp + WSIZE * index, bp);
        PUT_PTR(bp, NULL);
        PUT_PTR((unsigned int *)bp + 1, NULL);
    } else {
        PUT_PTR(bp, GET_PTR(heap_listp + WSIZE * index));
        PUT_PTR(GET_PTR(heap_listp + WSIZE * index) + 1, bp);  
        PUT_PTR((unsigned int *)bp + 1, NULL);
        PUT_PTR(heap_listp + WSIZE * index, bp);
    }
}

 可以看出,空闲块的前8个字节用来存放指向freelist对应位置的指针和指向下一个相同大小的空闲块的指针。当执行malloc时,这两个指针会被清除

/*
 * place - place the requested block at the beginning of the free block
 */
void place(void *bp, size_t asize)
{
    size_t csize = GET_SIZE(HDRP(bp));
    delete_list(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));
        insert_list(bp);
    } else {
        PUT(HDRP(bp), PACK(csize, 1));
        PUT(FTRP(bp), PACK(csize, 1));
    }
}

delete_list的实现如下

/* 
 * delete_list - 删除链表结点
 */
void delete_list(void *bp)
{
    int index;
    size_t size;
    size = GET_SIZE(HDRP(bp));
    index = getListOffset(size);
    if (GET_PTR(bp) == NULL && GET_PTR((unsigned int *)bp + 1) == NULL) { 
        /* 链表中唯一结点 */
        PUT_PTR(heap_listp + WSIZE * index, NULL);
    } else if (GET_PTR(bp) == NULL && GET_PTR((unsigned int *)bp + 1) != NULL) {
        /* 链表中最后一个结点, 不是唯一一个 */
        PUT_PTR(GET_PTR((unsigned int *)bp + 1), NULL);
    } else if (GET_PTR(bp) != NULL && GET_PTR((unsigned int *)bp + 1) == NULL){
        /* 链表中第一个结点, 不是唯一一个 */
        PUT_PTR(heap_listp + WSIZE * index, GET_PTR(bp));
        PUT_PTR(GET_PTR(bp) + 1, NULL);
    } else if (GET_PTR(bp) != NULL && GET_PTR((unsigned int *)bp + 1) != NULL) {
        /* 链表中的中间结点 */
        PUT_PTR(GET_PTR((unsigned int *)bp + 1), GET_PTR(bp));
        PUT_PTR(GET_PTR(bp) + 1, GET_PTR((unsigned int*)bp + 1));
    }
}

这种方法很巧妙,不需要额外的空间维护freelist,需要的空间只是在初始化时创建的freelist头,其它指针只存在于空闲块中,并在malloc时删除。这个方法主要参考这里

posted @ 2016-05-01 18:05  你好呵呵  阅读(694)  评论(0编辑  收藏  举报