<> 第五章 內存管理

十舍七匹狼於 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 字節的內存塊,由一個塊描述符指向。
假如空閒鏈表中沒有大於64的空閒內存塊,此時須要從新分配一個塊加到空閒鏈表上,系統分配的內存就是
(ptr = malloc(nbytes + NALLOC))
就是64 + 4096, NALLOC在x86機器上是4096. 而後將64個字節給到這個請求,那麼這個空閒鏈表上可用的內存就剩下了 4096 個字節了。
一段時間後,應用程序再次發起一個 4078 字節的請求,庫函數再次提高其到 4080 個字節。根據 Mem_alloc 的分配規則,if (bp->size > nbytes)
 知足這個條件,只是從空閒鏈表分配內存,而不會再分配大內存塊,分配完畢後空閒鏈表中的這個空閒節點的可利用內存就剩下了 sizeof(union align) 個字節了。而這 sizeof(union align) 個字節就是題中所說的留在空閒鏈表中的空閒塊,它成爲了一塊之後永不會用到的內存碎片。
可是想要真正的釋放這塊內存,必須使用標準庫的 free 函數,而記錄分配這整個 (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 個, 因此沒法單獨釋放某一個 */
            }
        }
    }
}



5.4
這個題目以爲缺乏上下文,大體的意思就是下面的代碼。
若是提供了打開的日誌指針,會好寫點。另外,感受這兒的option參數用的很差,這種方法判斷是哪一個函數很笨拙。
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 ;
}



5.5
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);
}
相關文章
相關標籤/搜索