最近在系统级程序设计的实验课上,老师布置了这项做业,通过几天的尝试,终于作出来了。下面来简单的回顾一下此次做业。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 一枝猪
转载请注明出处