SSD6 Exercise3總結

SSD6 Exercise3學習報告

最近在系統級程序設計的實驗課上,老師佈置了這項做業,通過幾天的嘗試,終於作出來了。下面來簡單的回顧一下此次做業。windows

最終結果

操做系統

  • Macintosh

Tips

  • 在開始作以前,因爲實在Mac下進行的編碼,因此說要將driver.c中的#include <windows.h>改成#include <unistd.h>
  • 接着開始編碼

具體流程

1

根據ex3.pdfImplementation Details中所描述的,咱們須要把memory block設計成以下所述的結構函數

因此說,最開始的雛形就有了學習

int allocatedSize = 0;          //存儲總共分配的大小

typedef struct Header HEADER;
struct Header{
    int checkSum;       //判斷是否發生了 ERROR #3
    int size;       //存儲MALLOC()中的大小
    long fence; //存儲0xCCDEADCC 做爲判斷是否發生ERROR #1 
};

typedef struct Footer FOOTER;
struct Footer{
    long fence;  //存儲0xCCDEADCC 做爲判斷是否發生了ERROR #2
};

typedef struct Block BLOCK;
struct Block{
    HEADER h; //頭部信息
    void *ptr; //Payload
    FOOTER f; //尾部信息
    char *filename;
    int linenumber;
    BLOCK *next;
}

2

因爲咱們要爲上述內容分配空間,因此說咱們在進行以下操做測試

#define HEADERSIZE sizeof(HEADER)
#define FOOTERSIZE sizeof(FOOTER)
#define BLOCKSIZE sizeof(BLOCK)
#define BARRIER 0xCCDEADCC

3

下面咱們開始思考如何實現MyMalloc,它的函數原型以下編碼

void *MyMalloc(size_t size, char *filename, int linenumber)操作系統

因爲內存地址對齊,因此說根據這個題,我先寫一個函數來讓size爲8的整數倍,多出來的內容須要用BARRIER填充debug

int align2Eight(int size)
{
    if( (size & 00x7) == 0)         
    {
        return size;
    }
    else
    {
        return ((size >> 3) + 1) << 3;
    }
}

個人想法是先設定一個鏈表,存儲BLOCK *,來管理這部份內容設計

因此說,咱們這時候須要一個鏈表的頭指針。指針

BLOCK *block_list_header = NULL調試

以後咱們須要實現關於這個鏈表的增、刪、查操做

查找最簡單,因此咱們先來實現查找

BLOCK *find_block(void *ptr)
{
    BLOCK *head = block_list_header;
    while(head)
    {
        if(head->ptr == ptr)
        {
            return head;
        }
        head = head->next;  
    }
    return NULL;    
}

而後咱們須要實現增, 須要注意一點是因爲BLOCK中包含char *filename,因此說別忘了爲它分配內存和釋放內存

int add_block(void *ptr, char *filename, int linenumber)
{
    //經過指針操做,來獲取HEADER信息
    HEADER h = *((HEADER *)ptr - 1);        //!注意 這裏的-1操做實際上是將ptr(地址值)減去了一個HEADERSIZE(16);
                                            // 若是你這樣操做(char *)ptr - 1, 這樣確實是讓地址值減去了1。這裏很關鍵,後面經常使用到
    int size = h->size;
    //經過指針操做獲取FOOTER信息
    FOOTER f = *(FOOTER *)((char *)ptr + align2Eight(size));
    
    //爲BLOCK分配內存
    BLOCK *curr = malloc(BLOCKSIZE);
    if(!curr)
    {
        return -1;
    }
    //爲filename分配內存
    int len = strlen(filename) + 1;     //存儲'\0'
    char *name = (char *)malloc((size_t)len);
    if(!name)
    {
        return -1;
    }
    strcpy(name, filename);
    
    curr->h = h;
    curr->ptr = ptr;
    curr->f = f;
    curr->filename = name;
    curr->linenumber = linenumber;
    curr->next = block_list_header;
    block_list_header = curr;
    
    return 0;
}

最後來實現一下刪操做

int remove_block(void *ptr)
{
    BLOCK *head = block_list_header;
    BLOCK *last;        //存儲需刪除的上一個指針
    while(head)
    {
        if(head->ptr == ptr)
        {
            if(last)    //若是不是表頭結點
            {
                last->next = head->next;
            }
            else
            {
                //挪動表頭結點
                block_list_header = block_list_header->next;
            }
            free(head->filename);
            free(head);
            return 0;
        }
        last = head;
        head = head->next;
    }
    return -1;  
}

最後,來寫一下求checkSum的函數

int get_check_number(long num)
{
    int count = 0;
    int standard = 0x1;
    int i;
    for(i = 0; i < sizeof(long); i++)
    {
        if((num & standard) == 1)
        {
            count ++;
        }
        num = num >> 1;
    }

    return count;
}

4

到此爲止,對於鏈表的基本操做都已經完成,下面我開始完成對MyMalloc函數的編寫

void *MyMalloc(size_t size, char *filename, int linenumber) {
    char *header_begin;
    char *footer;
    HEADER h;
    FOOTER f;
    int extra_size;         //內存對齊的填充大小
    long barrier = BARRIER:
    
    header_begin = (char *)malloc(HEADERSIZE + align2Eight((int)size) + FOOTERSIZE);
    if(!header_begin)
    {
        return NULL;
    }
    
    h.size = (int)size;
    h.fence = barrier;
    h.checkSum = get_check_number(h.size + h.fence);
    
    memcpy(header_begin, &h, HEADERSIZE);
    
    f.fence = barrier;
    footer = header_begin + HEADERSIZE + align2Eight(h.size);
    memcpy(footer, &f, FOOTERSIZE);
    
    //填充extra_size來判斷是否越界
    extra_size = align2Eight(h.size) - h.size;
    if(extra_size <= 4)
    {
        strncpy(footer - extra_size, (char *) &barrier, (size_t)extra_size);
    }
    else
    {
        strncpy(footer - extra_size, (char *) &barrier, 4);
        strncpy(footer - extra_size + 4, (char *) &barrier, (size_t)(extra_size - 4));
    }
    
    if(add_block((void *)(header_begin + HEADERSIZE), filename, linenumber) == 0)
    {
        allocatedSize += h.size;
        return (void *)(header_begin + HEADERSIZE);
    }
    else
    {
        free(header_begin);
        return NULL;
    }
    
}

在實現MyFree(void )以前,咱們須要判斷一下它是否有錯誤,因此說咱們在寫一個check_block(void ptr)的函數。

int check_block(void *ptr)              //返回值爲錯誤類型
{
    HEADER *h = (HEADER *)ptr - 1;
    long fence = BARRIER;
    int size = h->size;
    int checkSum = h->checkSum;
    long h_fence = h->fence;
    int extra_size = align2Eight(size) - size;
    
    FOOTER *f = (FOOTER *)(char *)ptr + align2Eight(size));
    long f_fence = f->fence;
    
    //先判斷ERROR #2
    if (extra_size <= 4)
    {
        if(strncmp((char *)f - extra_size, (char *) &fence, (size_t)extra_size) != 0)
        {
            return 2;
        }
    }
    else{
        if(strncmp((char *)f - extra_size, (char *) &fence, 4) != 0
                || strncmp((char *)f - extra_size + 4, (char *)&fence, (size_t)(extra_size - 4)) != 0)
        {
            return 2;
        }
    }
    
    if(f_fence != BARRIER)
    {
        return 2;
    }
     else if(h_fence != BARRIER)
     {
        return 1;
     }
     else if(checkSum != get_check_number(size + h_fence))
     {
        return 3;
     }
     
     return 0;
   
}

好了,咱們最後來實現一下MyFree(void *)函數

void MyFree(void *ptr, char *filename, int linenumber) {
    int num;
    HEADER *h = (HEADER *)ptr - 1;
    BLOCK *curr = NULL;
    curr = find_block(ptr);
    if(!curr)
    {
        error(4, filename, linenumber);
    }
    else{
        num = check_block(ptr);
        if(num)
        {
            errorfl(num, curr->filename, curr->linenumber, filename, linenumber);
        }

        allocatedSize -= h->size;
        free(h);
        remove_block(ptr);
    }
}

5

截止到目前,大部分都已經完成,最後再把Bonus實現,便大功告成。

/* returns number of bytes allocated using MyMalloc/MyFree:
    used as a debugging tool to test for memory leaks */
int AllocatedSize() {
    return allocatedSize;
}



/* Optional functions */

/* Prints a list of all allocated blocks with the
    filename/line number when they were MALLOC'd */
void PrintAllocatedBlocks() {
    BLOCK *curr = block_list_header;
    while(curr)
    {
        PRINTBLOCK(curr->h.size, curr->filename, curr->linenumber);
        curr=curr->next;
    }
}

/* Goes through the currently allocated blocks and checks
    to see if they are all valid.
    Returns -1 if it receives an error, 0 if all blocks are
    okay.
*/
int HeapCheck() {
    BLOCK *curr = block_list_header;
    int result = 0, err;
    while(curr)
    {
        err = check_block(curr->ptr);
        if(err)
        {
            result = -1;
            PRINTERROR(err, curr->filename, curr->linenumber);
        }
        curr = curr->next;
    }
    return result;
}

Finish

OK, 到此爲止,但願對你有幫助

補充

下面對一些具體的測試用例使用GDB調試觀察地址及其內容

Test case2

咱們知道,字符串str在分配內存時應分配8+1('\0')大小的內存。因此說這個BLOCKFooter裏面的fence應該被改寫了,讓咱們來看一些內存中的內容

//首先生成可用gdb調試的版本
gcc -g *.c -o debugmalloc

在檢查出錯的函數check_block加入斷點

使用-t參數運行第二個測試用例

咱們看到斷點發生在debugmalloc.c:135行,此時ptr的地址值爲0x100300070

注意:由於對generic pointer(void *)強制轉化爲HEADER *,而且h_p = (HEADER *)ptr - 1,這裏面的-1實際上減去了1 * sizeof(HEADER),也就是減去了16

咱們繼續用n來讓程序繼續往下單步運行

因爲該用例只分配了8個內存,而且8是8的整數倍,不須要分配額外的內存來使內存對齊,因此說咱們看到的h_p的地址爲0x100300078,比ptr8字節,可是fence的值和f_fence的值不一樣,說明尾部信息被篡改了,即分配內存不足,接下來讓咱們繼續運行程序,獲得相應的結果

符合上面的正確答案。


再讓咱們看一個頭部信息被改變的實例

Test case6

先看下面這行代碼

//把ptr低8字節的地址中的int數據改成 8 + (1 << 31);
*((int *)(ptr - 8)) = 8 + (1 << 31);

這時候咱們再看咱們最初定義的Header結構

struct Header{
    int checkSum;
    int size;
    long fence;
}

很明顯,他是想經過更改Header中的fence來出錯,那讓咱們用gdb跟蹤調試一下,看個究竟。

繼續執行,獲得最終結果

符合上面的答案。


最後補充

假設咱們分配了兩次,沒有進行free,整個程序的內部結構大概爲下圖

by 一枝豬

轉載請註明出處

相關文章
相關標籤/搜索