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时删除。这个方法主要参考这里。