1. 程式人生 > >linux下malloc()和free()的原理及實現

linux下malloc()和free()的原理及實現

在學習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()實現流程圖如下所示:

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;
}