十舍七匹狼於~ http://www.oschina.net/ 編程
5.1 app
一種好的位模式,數值上來講應該較大,一旦執行到該內存處,應該會很容易肉眼發現。可使用好比 0XCC, 0XDC 之類的。修改 Mem_alloc 函數,將它初始化爲 0XCC 的話,須要在 函數
if ((ptr = malloc(nbytes + NALLOC)) == NULL這句判斷完整結束的後面添加上
memset(ptr, 0xCC, nbytes + NALLOC);尋找可以捕獲一個bug的應用程序的話,比較簡單的作法是, 好比分配100字節:
void *data = Mem_alloc(100, __FILE__, __LINE__); /* 沒有初始化, 直接取得其中的數據, 會出來一個燙字 */ printf("%c%c\n", *data, *(data + 1));
以上關於 0XCC 引自<<編程精粹>>一書。 spa
5.2
我認爲這個小於等於 sizeof(union align) 的內存塊難以釋放。
好比第一次須要經過malloc函數分配了 50 個字節給ptr, 事實上第一次經過 Mem_alloc 函數會分配 64 個字節,由於 .net
nbytes = ((nbytes + sizeof (union align) - 1)/ (sizeof (union align)))*(sizeof (union align));這行代碼會將 nbytes 提高到 sizeof(union align) 的整數倍,而在Linux的x86上
sizeof(union align) == 16;此時須要系統分配了一個 64 字節的內存塊,由一個塊描述符指向。
(ptr = malloc(nbytes + NALLOC))就是64 + 4096, NALLOC在x86機器上是4096. 而後將64個字節給到這個請求,那麼這個空閒鏈表上可用的內存就剩下了 4096 個字節了。
想要釋放這塊內存,必需要知道除了這16個字節外的剩餘全部字節此時是否都處於空閒狀態,而後才能調用 free(bp->ptr) 函數,整塊的釋放這 (64 + 4096) 個字節。若是不判斷剩餘字節的空閒狀態就釋放內存,極可能之後的操做會操做非法內存。爲何 free(bp->ptr) 不能只是釋放這16個字節?由於 Mem_alloc 函數只是對 malloc 函數的再封裝,並不能真正的爲系統底層函數記錄當前內存狀態, Mem_alloc 函數是一種高層的抽象。 指針
我認爲這個稽覈實現雖然有如下的優勢:
1.不會返回同一地址2次
2.不會釋放一個不是由稽覈實現所分配的內存塊
3.要釋放或是要分配的內存不會是存在衝突的, 由於這些內存塊要麼空閒, 要麼已分配
可是它有個難以抹平的缺點, 那就是沒法真正在系統中釋放這些內存. 它們開始分配好的內存會被逐漸切割, 致使難以釋放. 分配的塊描述符是一次性的分配 NDESCRIPTORS 個, 以後慢慢使用這 NDESCRIPTORS 塊內存, 致使難以得到起始分配的 NDESCRIPTORS 內存的地址, 致使了塊描述符也是難以釋放。
日誌
5.3
想要合併空閒鏈表中相鄰的空閒塊,從freelist這個空閒鏈表的某一個節點1開始搜索,搜索一圈返回至本節點1結束,比較 ptr + bp->size 和freelist中其他節
點的ptr是否相等,若是相等就合併這兩個空閒節點1和節點2。合併的步驟是:節點1中的size擴張爲1和2中的和,在空閒鏈表中刪除節點2,而後在hash表中去掉節點2 code
/* 在hash表中刪除某節點, 可是不釋放內存, 由於它們還存在於freelist鏈表中 */ void Node_delete(void *ptr) { struct descriptor **pp; for (pp = &htab[hash(ptr, htab)]; *pp; pp = &(*pp)->link) if ((*pp)->ptr == ptr) { struct descriptor *p = *pp; *pp = p->link; /* 這兒沒法作簡單的free(ptr)操做, 要知道ptr老是和一些複雜的數據關聯着 */ return ; } } /* 2個循環, 第一個循環遍歷空閒鏈表中的節點 第二個循環尋找那些地址在某個節點下面的節點,而後在空閒鏈表中合併它們 */ void Mem_gather(void) { struct descriptor *bp; for (bp = freelist.free; bp != &freelist; bp = bp->free) { struct descriptor *p; for (p = bp->free; p != bp; p = p->free) { if ((bp->ptr + bp->size) == p->ptr) { struct descriptor *h = p->free; bp->size += p->size; bp->free = h; Node_delete(ptr); /* 可是描述符指向的內存塊是無法釋放的, 它們一次會分配 NDESCRIPTORS 個, 因此沒法單獨釋放某一個 */ } } } }
void Mem_log(FILE *log, void *ptr, const char *file, int line, int option) { if (log != NULL) { struct descriptor bp = find(ptr); switch(option): case 1: fprintf(log, "** freeing free memory\n");\ fprintf(log, "Mem_free(%0x) called from %s: %d\n", ptr, file, line); fprintf(log, "This block is %d bytes long and was allocated from %s: %d\n", bp->size, bp->file, bp->line); case 2: fprintf(log, "** resizing unallocated memory\n");\ fprintf(log, "Mem_resize(%0x, %d) called from %s: %d\n", ptr, file, line); } else Except_raise(&Assert_Failed, file, line); return ; }
void Mem_leak(apply(void *ptr, long size, const char *file, int line, void *cl), void *cl) { int i; struct descriptor *bp; for (i = 0; i < sizeof(htab)/sizeof(htab[0]); i++) for (bp = htab[i]; bp; bp = bp->link) /* 題目要求, 對每個已分配的內存塊調用apply函數 */ if (bp->free != NULL) apply(bp->ptr, bp->size, bp->file, bp->line, cl); }