详解malloc,calloc,realloc原理及其模拟实现
malloc原理
malloc它有一個將可用的內存塊連接為一個長長的列表的所謂空閑鏈表。調用malloc函數時,它沿連接表尋找一個大到足以滿足 用戶請求所需要的內存塊。然后,將該內存塊一分為二(一塊的大小與用戶請求的大小相等,另一塊的大小就是剩下的字節)。接下來,將分配給用戶的那塊內存傳 給用戶,并將剩下的那塊(如果有的話)返回到連接表上。調用free函數時,它將用戶釋放的內存塊連接到空閑鏈上。到最后,空閑鏈會被切成很多的小內存片 段,如果這時用戶申請一個大的內存片段,那么空閑鏈上可能沒有可以滿足用戶要求的片段了。于是,malloc函數請求延時,并開始在空閑鏈上翻箱倒柜地檢 查各內存片段,對它們進行整理,將相鄰的小空閑塊合并成較大的內存塊。
查詢鏈表的方法:
break指針
Linux維護一個break指針,這個指針指向堆空間的某個地址。從堆起始地址到break之間的地址空間為映射好的,可以供進程訪問;而從break往上,是未映射的地址空間,如果訪問這段空間則程序會報錯。我們用malloc進行內存分配就是從break往上進行的。
First fit:從頭開始,使用第一個數據區大小大于要求size的塊所謂此次分配的塊。首次適配有更好的運行效率。
Best fit:從頭開始,遍歷所有塊,使用數據區大小大于size且差值最小的塊作為此次分配的塊。最佳適配具有較高的內存使用率。
brk將break指針直接設置為某個地址;
而sbrk將break從當前位置移動increment所指定的增量,如果將increment設置為0,則可以獲得當前break的地址。
malloc實現:
void* malloc(unsigned size); 在堆內存中分配一塊長度為size字節的連續區域,參數size為需要內存空間的長度。
#include <sys/types.h> #include <unistd.h>typedef struct s_block *t_block;struct s_block {size_t size; // 數據區大小 t_block next; // 指向下個塊的指針 int free; // 是否是空閑塊 int padding; // 填充4字節,保證meta塊長度為8的倍數 char data[1] // 這是一個虛擬字段,表示數據塊的第一個字節,長度不應計入meta };//首次適配 t_block find_block(t_block *last, size_t size) {t_block b = first_block;while(b && !(b->free && b->size >= size)) {*last = b;b = b->next;}return b; }//如果現有block都不能滿足size的要求, //則需要在鏈表最后開辟一個新的block。 //這里關鍵是如何只使用sbrk創建一個struct#define BLOCK_SIZE 24 //由于存在虛擬的data字段,sizeof不能正確計算meta長度,這里手工設置 t_block extend_heap(t_block last, size_t s) {t_block b;b = sbrk(0);if(sbrk(BLOCK_SIZE + s) == (void *)-1)return NULL;b->size = s;b->next = NULL;if(last)last->next = b;b->free = 0;return b; }//First fit有一個比較致命的缺點, //就是可能會讓很小的size占據很大的一塊block, //此時,為了提高payload,應該在剩余數據區足夠大的情況下,將其分裂為一個新的block,void split_block(t_block b, size_t s) {t_block newb;newb = b->data + s;newb->size = b->size - s - BLOCK_SIZE ;newb->next = b->next;newb->free = 1;b->size = s;b->next = newb; }//由于我們希望malloc分配的數據區是按8字節對齊, //所以在size不為8的倍數時,我們需要將size調整為大于size的最小的8的倍數: size_t align8(size_t s) {if(s & 0x7 == 0)return s;return ((s >> 3) + 1) << 3; }void *first_block=NULL;void *malloc(size_t size) {t_block b, last;size_t s;/* 對齊地址 */s = align8(size);if(first_block) {/* 查找合適的block */last = first_block;b = find_block(&last, s);if(b) {<pre name="code" class="cpp"> /* 如果可以,則分裂 */if ((b->size - s) >= ( BLOCK_SIZE + 8))split_block(b, s);b->free = 0;} else {/* 沒有合適的block,開辟一個新的 */b = extend_heap(last, s);if(!b)return NULL;}} else {b = extend_heap(NULL, s);if(!b)return NULL;first_block = b;}return b->data; }calloc實現:
void* calloc(size_t numElements, size_t sizeOfElement);
與malloc相似,參數sizeOfElement為單位元素長度(例如:sizeof(int)),numElements為元素個數,即在內存中申請numElements * sizeOfElement字節大小的連續內存空間。并且會把內存初始化為0。
calloc(num, size) 基本上等于 void *p = malloc(num * size); memset(p, 0, num * size); 但理論上 calloc 的實現可避免 num * size 溢出,當溢出時返回 NULL 代表失敗,而 malloc(num * size) 可能會分配了一個尺寸溢出后的內存。
由于我們的數據區是按8字節對齊的,所以為了提高效率,我們可以每8字節一組置0,而不是一個一個字節設置。我們可以通過新建一個size_t指針,將內存區域強制看做size_t類型來實現。
void *calloc(size_t number, size_t size) {size_t *news;size_t s8, i;news = malloc(number * size);if(news) {s8 = align8(number * size) >> 3;for(i = 0; i < s8; i++)news[i] = 0;}return news; }realloc實現:
void* realloc(void* ptr, unsigned newsize);使用realloc函數為ptr重新分配大小為size的一塊內存空間。下面是這個函數的工作流程:
free實現:
地址應該在之前malloc所分配的區域內,即在first_block和當前break指針范圍內;
這個地址確實是之前通過我們自己的malloc分配的。
將block和相鄰block合并。為了滿足這個實現,需要將s_block改為雙向鏈表。修改后的block結構如下:
typedef struct s_block *t_block; struct s_block {size_t size; /* 數據區大小 */t_block prev; /* 指向上個塊的指針 */t_block next; /* 指向下個塊的指針 */int free; /* 是否是空閑塊 */int padding; /* 填充4字節,保證meta塊長度為8的倍數 */void *ptr; /* Magic pointer,指向data */char data[1] /* 這是一個虛擬字段,表示數據塊的第一個字節,長度不應計入meta */ }; #define BLOCK_SIZE 28合并方法如下:
t_block fusion(t_block b) {if (b->next && b->next->free) {b->size += BLOCK_SIZE + b->next->size;b->next = b->next->next;if(b->next)b->next->prev = b;}return b; } void free(void *p) {t_block b;if(valid_addr(p)) {b = get_block(p);b->free = 1;if(b->prev && b->prev->free)b = fusion(b->prev);if(b->next)fusion(b);else {if(b->prev)b->prev->prev = NULL;elsefirst_block = NULL;brk(b);}} }總結
以上是生活随笔為你收集整理的详解malloc,calloc,realloc原理及其模拟实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 模拟实现priority_queue优先
- 下一篇: 大力女都奉顺剧情介绍