最近在系統級程序設計的實驗課上,老師佈置了這項做業,通過幾天的嘗試,終於作出來了。下面來簡單的回顧一下此次做業。windows
#include <windows.h>
改成#include <unistd.h>
根據ex3.pdf
中Implementation 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; }
因爲咱們要爲上述內容分配空間,因此說咱們在進行以下操做測試
#define HEADERSIZE sizeof(HEADER) #define FOOTERSIZE sizeof(FOOTER) #define BLOCKSIZE sizeof(BLOCK) #define BARRIER 0xCCDEADCC
下面咱們開始思考如何實現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; }
到此爲止,對於鏈表的基本操做都已經完成,下面我開始完成對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); } }
截止到目前,大部分都已經完成,最後再把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; }
OK, 到此爲止,但願對你有幫助
咱們知道,字符串str
在分配內存時應分配8+1('\0')
大小的內存。因此說這個BLOCK
中Footer
裏面的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
,比ptr
高8
字節,可是fence
的值和f_fence
的值不一樣,說明尾部信息被篡改了,即分配內存不足,接下來讓咱們繼續運行程序,獲得相應的結果
符合上面的正確答案。
再讓咱們看一個頭部信息被改變的實例
先看下面這行代碼
//把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 一枝豬
轉載請註明出處