1. 程式人生 > >malloc calloc realloc free的簡單實現

malloc calloc realloc free的簡單實現

寫在前面:csdn的部落格排版就是shit,祝早日關門大吉

記憶體分配其實是個必修課,應該清楚地知道一個程式在計算機中的記憶體分佈情況,linux程式在記憶體中的分佈情況是這樣的:
老子也不知道這圖從哪來的

當然啦除了知道諸如“堆從低地址向高地址增長棧從高地址從低地址增長”這種東西之外最好還要知道“什麼是分頁為什麼分頁malloc的內部實現用到了brk()sbrk()MMU是什麼”之類的這種東西。

切入正題,一個簡單的malloc實現如下:

#include <stdio.h>
#include "unistd.h"
#define BLOCK_SIZE 40

typedef struct s_block *t_block;
void
*first_block=NULL; t_block find_block(t_block *last,size_t size); t_block extend_heap(t_block last, size_t s); void split_block(t_block b,size_t s); size_t align8(size_t s); void *malloc(size_t size); void *calloc(size_t number, size_t size); t_block get_block(void *p); int valid_addr(void *p); t_block fusion(t_block b); void
free(void *p); void copy_block(t_block src,t_block dst); void *realloc(void *p,size_t size); struct s_block{ size_t size;//資料區大小 t_block prev;//指向上個塊的指標 t_block next;//指向下個塊的指標 int free;//判斷是否是空閒塊 int padding;//填充4位元組,為了迎合結構體對齊,保證meta塊長度為8的倍數 void *ptr;//Magic pointer,指向data //虛擬欄位這個是真的巧妙!每個塊的s_block後面都是資料區,但是這個data[]不算作s_block的內容
//不計作s_block的長度,所以在訪問s_block的data欄位時實際上訪問的是資料區的第一個位元組 char data[1]; }; int main(void){ return 0; } t_block find_block(t_block *last,size_t size){ t_block b=first_block; while (b && !(b->free && b->size >= size)) { *last=b;//last用來表示最後一塊可用的記憶體塊(可能是剛剛被釋放過之後的一個塊) b=b->next; } return b; } t_block extend_heap(t_block last, size_t s){ t_block b;//強制把新申請出來的記憶體看做是s_block型別的 b = sbrk(0); if (sbrk(BLOCK_SIZE + s) == (void *) -1) { return NULL; } b->size=s; b->next=NULL; b->ptr = &(b->data);//data是一個數組所以其實不用加& if(last){ last->next=b; } b->free=0; return b; } void split_block(t_block b,size_t s){ t_block new; new=b->data+s; new->size = b->size - s - BLOCK_SIZE; new->next = b->next; new->free=1; b->size=s; b->next = new; } size_t align8(size_t s){ if (s & 0x7 == 0) {//滿足該條件的s是可以被8整除的 return s; } //這樣可以得到一個大於s且能夠被8整除的最小的數 return ((s>>3)+1)<<3; } void *malloc(size_t size){ t_block b,last; size_t s = align8(size);//地址對齊 if (first_block) { last=first_block;// b = find_block(&last, s); if(b){ if ((b->size) >= (BLOCK_SIZE + 8)) { split_block(b, s); } b->free=0; } else { 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; } void *calloc(size_t number, size_t size){ size_t *new; size_t s8; new = malloc(number * size); if (new) { s8=align8((number*size))>>3; for (int i = 0; i < s8; i++) { new[i]=0; } } return new;; } t_block get_block(void *p){ char *tmp; tmp=p; return (p = tmp -= BLOCK_SIZE); } int valid_addr(void *p){ if (first_block) { if(p<sbrk(0)&&p>first_block){ return p==(get_block(p))->ptr;//?????? } } return 0; } 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){//傳進來的p實際上是那個block的資料區 t_block b; if(valid_addr(p)){ b = get_block(p);//b此時指向那塊block的起始地址 b->free=1;//只是標記為空閒,實際上data區還是有資料的 if (b->prev && b->prev->free) { b=fusion(b->prev); } if (b->next) {//檢查當前block是否還有後繼,即檢查是否是最後一個block fusion(b);//如果有則嘗試合併 }else{//如果沒有後繼則先檢查是否是第一個block if (b->prev) {//檢查是否有前驅,如果有前驅則表示該block不是第一個 b->prev->next = NULL; }else{//如果沒有前驅那麼該block既是第一個也是最後一個block first_block = NULL;//又回到了任何一次malloc之前 } brk(b);//用brk在heap上進行回退 } } } void copy_block(t_block src,t_block dst){ size_t *sdata, *ddata; sdata=src->ptr; ddata=dst->ptr; for (size_t i = 0;(i*8)<src->size&&(i*8)<dst->size; i++) { ddata[i] = sdata[i]; } } void *realloc(void *p,size_t size){ size_t s; t_block b, new; void *newp; if(!p){ return malloc(size); } if(valid_addr(p)){ s = align8(size); b = get_block(p);//p是資料區起始,此時b指向那整塊記憶體的起始 if(b->size>=s){//如果這個塊能直接放下原資料大小則對資料什麼也不做 if(b->size-s>=(BLOCK_SIZE+8)){//並順手檢查一下是否可以切割記憶體 split_block(b, s); } }else{//如果放不下則檢查一下是否可以合併,允許合併的條件的最後一條是合併之後能放下資料 if (b->next && b->next->free && b->next->size + b->size+BLOCK_SIZE >= s) { fusion(b); //如果可以合併則在合併完之後檢查是否可以進行分裂 if(b->size-s>=(BLOCK_SIZE+8)){ split_block(b, s); } }else{//如果既放不下資料也不符合合併的條件 //那麼就malloc一塊新記憶體 newp = malloc(s); if(!newp){ return NULL; } new = get_block(newp); copy_block(b, new); free(p); return newp; } } return (p); } return NULL; }

這段程式碼不是我的原創,我讀過去之後加了一些註釋方便理解。引用到的一些程式碼在這裡
http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf
只說說閱讀這段程式碼的感受吧。
底層程式碼寫的真的是滴水不漏啊,看的過程中給我一種很嚴謹的感覺。
程式碼中有一些比較取巧的寫法,比如結構體裡的虛擬成員,為了驗證地址是否合法而加的magic pointer,釋放記憶體時不是對記憶體進行覆蓋而是在開頭的指示塊的free中標記為空閒。為了效率在程式碼中多次使用位運算。
總之閱讀這段程式碼收穫還是挺大的。