在作csapp的malloc實驗,一開始是按照書上的隱式鏈表法,發現得分很低。這種方法確實很挫,須要遍歷一遍以找到合適的空閒塊。因而我想到《STL源碼剖析》中stl的內存池,感受應該能夠用相似的方法作,由於malloc要作的事情實際就是爲了防止內存碎片和減小系統調用,實際就是一個內存池。可是書上介紹的stl的內存池是沒有邊界區塊的,也就是沒辦法合併空閒區塊,這樣可能產生過多的外部碎片。因此我最終的作法是大致上採用stl的方法,再加上邊界區塊,以方便合併空閒區塊。數組
首先是根據須要的大小在freelist中找到合適的位置app
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的操做以下spa
/* * 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時,這兩個指針會被清除.net
/* * 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時刪除。這個方法主要參考這裏。code