在學習C語言的時候知道了動態記憶體分配的概念,也知道了malloc()的使用方式,但是一直沒有去瞭解或者認真學習malloc()的實現原理。今天看到關於動態記憶體分配方面的資料,就整理總結下。
在C語言中只能通過malloc()和其派生的函數進行動態申請記憶體,而實現的根本是通過系統調用實現的(在linux下是通過sbrk()系統調用實現),這次的總結也是基於linux系統。
在說明malloc()的實現思路之前先要說下在linux系統下是怎麼程式中的堆的。在linux系統下面一個程式的堆的管理是通過記憶體塊進行管理的,也就是將堆分成了很多大小不一的記憶體塊。這些塊怎麼管理尼,比如怎麼查詢塊的大小,怎麼查詢塊是否正在被程式使用,怎麼知道這個塊的地址。為瞭解決記憶體塊的管理所以要設計一個管理記憶體塊的資料結構,詳細的資料結構如下:
/**記憶體控制塊資料結構,用於管理所有的記憶體塊* is_available: 標誌著該塊是否可用。1表示可用,0表示不可用* size: 該塊的大小**/struct mem_control_block { int is_available; int size;};
有了管理記憶體塊的資料結構,那麼在記憶體中堆的組織形式也好理解了,也就是堆是由很多記憶體塊組成的,所以有了如下的示意圖:
綜合上面的知識,可以很容易想到malloc()實現的大體思路。首先挨個檢查堆中的記憶體是否可用,如果可用那麼大小是否能滿足需求,要是都滿足的話就直接用。當遍曆了堆中的所有記憶體塊時,要是沒有能滿足需求的塊時就只能通過系統調用向作業系統申請新的記憶體,然後將新的記憶體添加到堆中。思路很簡單,malloc()實現流程圖如下所示:
看完上面的思路,也會很容易的想到free()函數的實現思路,只要將記憶體管理塊設定為可用就可以了。這樣下次調用malloc()函數的時候就可以將該記憶體塊作為可分配塊再次進行分配了。
最後,貼上malloc()和free()實現的代碼:
malloc()實現:
/**記憶體控制塊資料結構,用於管理所有的記憶體塊* is_available: 標誌著該塊是否可用。1表示可用,0表示不可用* size: 該塊的大小**/struct mem_control_block { int is_available; int size;};/**在實現malloc時要用到linux下的全域變數*managed_memory_start:該指標指向進程的堆底,也就是堆中的第一個記憶體塊*last_valid_address:該指標指向進程的堆頂,也就是堆中最後一個記憶體塊的末地址**/void *managed_memory_start;void *last_valid_address;/**malloc()功能是動態分配一塊滿足參數要求的記憶體塊*numbytes:該參數表明要申請多大的記憶體空間*傳回值:函數執行結束後將返回滿足參數要求的記憶體塊首地址,要是沒有分配成功則返回NULL**/void *malloc(size_t numbytes) { //遊標,指向當前的記憶體塊 void *current_location; //儲存當前記憶體塊的記憶體控制結構 struct mem_control_block *current_location_mcb; //儲存滿足條件的記憶體塊的地址用於函數返回 void *memory_location; memory_location = NULL; //計算記憶體塊的實際大小,也就是函數參數指定的大小+記憶體控制塊的大小 numbytes = numbytes + sizeof(struct mem_control_block); //利用全域變數得到堆中的第一個記憶體塊的地址 current_location = managed_memory_start; //對堆中的記憶體塊進行遍曆,找合適的記憶體塊 while (current_location != last_valid_address) //檢查是否遍曆到堆頂了 { //取得當前記憶體塊的記憶體控制結構 current_location_mcb = (struct mem_control_block*)current_location; //判斷該塊是否可用 if (current_location_mcb->is_available) //檢查該塊大小是否滿足 if (current_location_mcb->size >= numbytes) { //滿足的塊將其標誌為不可用 current_location_mcb->is_available = 0; //得到該塊的地址,結束遍曆 memory_location = current_location; break; } //取得下一個記憶體塊 current_location = current_location + current_location_mcb->size; } //在堆中已有的記憶體塊中沒有找到滿足條件的記憶體塊時執行下面的函數 if (!memory_location) { //向作業系統申請新的記憶體塊 if (sbrk(numbytes) == -1) return NULL;//申請失敗,說明系統沒有可用記憶體 memory_location = last_valid_address; last_valid_address = last_valid_address + numbytes; current_location_mcb = (struct mem_control_block)memory_location; current_location_mcb->is_available = 0; current_location_mcb->size = numbytes; } //到此已經得到所要的記憶體塊,現在要做的是越過記憶體控制塊返回記憶體塊的首地址 memory_location = memory_location + sizeof(struct mem_control_block); return memory_location;}
free()實現:
/**free()功能是將參數指向的記憶體塊進行釋放*firstbyte:要釋放的記憶體塊首地址*傳回值:空**/void free(void *firstbyte){ struct mem_control_block *mcb; //取得該塊的記憶體控制塊的首地址 mcb = firstbyte - sizeof(struct mem_control_block); //將該塊標誌設為可用 mcb->is_available = 1; return;}