malloc函數的一種簡單的原理性實現

// 轉載。還沒看明白,有待修改。
malloc()是C語言中動態存儲管理的一組標準庫函數之一。其做用是在內存的動態存儲區中分配一個長度爲size的連續空間。其參數是一個無符號整形數,返回值是一個指向所分配的連續存儲域的起始地址的指針
malloc()工做機制
malloc函數的實質體如今,它有一個將可用的內存塊鏈接爲一個長長的列表的所謂空閒鏈表。調用malloc函數時,它沿鏈接表尋找一個大到足以知足用戶請求所須要的內存塊。而後,將該內存塊一分爲二(一塊的大小與用戶請求的大小相等,另外一塊的大小就是剩下的字節)。接下來,將分配給用戶的那塊內存傳給用戶,並將剩下的那塊(若是有的話)返回到鏈接表上。調用free函數時,它將用戶釋放的內存塊鏈接到空閒鏈上。到最後,空閒鏈會被切成不少的小內存片斷,若是這時用戶申請一個大的內存片斷,那麼空閒鏈上可能沒有能夠知足用戶要求的片斷了。因而,malloc函數請求延時,並開始在空閒鏈上翻箱倒櫃地檢查各內存片斷,對它們進行整理,將相鄰的小空閒塊合併成較大的內存塊。
malloc()在操做系統中的實現
在 C 程序中,屢次使用malloc () 和 free()。不過,您可能沒有用一些時間去思考它們在您的操做系統中是如何實現的。本節將向您展現 malloc 和 free 的一個最簡化實現的代碼,來幫助說明管理內存時都涉及到了哪些事情。
在大部分操做系統中,內存分配由如下兩個簡單的函數來處理:
void *malloc (long numbytes):該函數負責分配 numbytes 大小的內存,並返回指向第一個字節的指針。
void free(void *firstbyte):若是給定一個由先前的 malloc 返回的指針,那麼該函數會將分配的空間歸還給進程的「空閒空間」。
malloc_init 將是初始化內存分配程序的函數。它要完成如下三件事:將分配程序標識爲已經初始化,找到系統中最後一個有效內存地址,而後創建起指向咱們管理的內存的指針。這三個變量都是全局變量:
 
        //清單 1. 咱們的簡單分配程序的全局變量
        int has_initialized = 0;
        void *managed_memory_start;
        void *last_valid_address;
如前所述,被映射的內存的邊界(最後一個有效地址)常被稱爲系統中斷點或者 當前中斷點。在不少 UNIX? 系統中,爲了指出當前系統中斷點,必須使用 sbrk(0) 函數。 sbrk 根據參數中給出的字節數移動當前系統中斷點,而後返回新的系統中斷點。使用參數 0 只是返回當前中斷點。這裏是咱們的 malloc 初始化代碼,它將找到當前中斷點並初始化咱們的變量:
 
清單 2. 分配程序初始化函數
/* Include the sbrk function */
 
#include 
void malloc_init()
{
/* grab the last valid address from the OS */
last_valid_address = sbrk(0);
/* we don''t have any memory to manage yet, so
 *just set the beginning to be last_valid_address
 */
managed_memory_start = last_valid_address;
/* Okay, we''re initialized and ready to go */
 has_initialized = 1;
}
如今,爲了徹底地管理內存,咱們須要可以追蹤要分配和回收哪些內存。在對內存塊進行了 free 調用以後,咱們須要作的是諸如將它們標記爲未被使用的等事情,而且,在調用 malloc 時,咱們要可以定位未被使用的內存塊。所以, malloc 返回的每塊內存的起始處首先要有這個結構:
 
//清單 3. 內存控制塊結構定義
struct mem_control_block {
    int is_available;
    int size;
};
如今,您可能會認爲當程序調用 malloc 時這會引起問題 —— 它們如何知道這個結構?答案是它們沒必要知道;在返回指針以前,咱們會將其移動到這個結構以後,把它隱藏起來。這使得返回的指針指向沒有用於任何其餘用途的內存。那樣,從調用程序的角度來看,它們所獲得的所有是空閒的、開放的內存。而後,當經過 free() 將該指針傳遞回來時,咱們只須要倒退幾個內存字節就能夠再次找到這個結構。
在討論分配內存以前,咱們將先討論釋放,由於它更簡單。爲了釋放內存,咱們必需要作的唯一一件事情就是,得到咱們給出的指針,回退 sizeof(struct mem_control_block) 個字節,並將其標記爲可用的。這裏是對應的代碼:
 
清單 4. 解除分配函數
void free(void *firstbyte) {
    struct mem_control_block *mcb;
/* Backup from the given pointer to find the
 * mem_control_block
 */
   mcb = firstbyte - sizeof(struct mem_control_block);
/* Mark the block as being available */
  mcb->is_available = 1;
/* That''s It!  We''re done. */
  return;
}
如您所見,在這個分配程序中,內存的釋放使用了一個很是簡單的機制,在固定時間內完成內存釋放。分配內存稍微困難一些。咱們主要使用鏈接的指針遍歷內存來尋找開放的內存塊。這裏是代碼:
 
//清單 6. 主分配程序
void *malloc(long numbytes) {
    /* Holds where we are looking in memory */
    void *current_location;
    /* This is the same as current_location, but cast to a
    * memory_control_block
    */
    struct mem_control_block *current_location_mcb;
    /* This is the memory location we will return.  It will
    * be set to 0 until we find something suitable
    */
    void *memory_location;
    /* Initialize if we haven''t already done so */
    if(! has_initialized) {
        malloc_init();
    }
    /* The memory we search for has to include the memory
    * control block, but the users of malloc don''t need
    * to know this, so we''ll just add it in for them.
    */
    numbytes = numbytes + sizeof(struct mem_control_block);
    /* Set memory_location to 0 until we find a suitable
    * location
    */
    memory_location = 0;
    /* Begin searching at the start of managed memory */
    current_location = managed_memory_start;
    /* Keep going until we have searched all allocated space */
    while(current_location != last_valid_address)
    {
    /* current_location and current_location_mcb point
    * to the same address.  However, current_location_mcb
    * is of the correct type, so we can use it as a struct.
    * current_location is a void pointer so we can use it
    * to calculate addresses.
        */
        current_location_mcb =
            (struct mem_control_block *)current_location;
        if(current_location_mcb->is_available)
        {
            if(current_location_mcb->size >= numbytes)
            {
            /* Woohoo!  We''ve found an open,
            * appropriately-size location.
                */
                /* It is no longer available */
                current_location_mcb->is_available = 0;
                /* We own it */
                memory_location = current_location;
                /* Leave the loop */
                break;
            }
        }
        /* If we made it here, it''s because the Current memory
        * block not suitable; move to the next one
        */
        current_location = current_location +
            current_location_mcb->size;
    }
    /* If we still don''t have a valid location, we''ll
    * have to ask the operating system for more memory
    */
    if(! memory_location)
    {
        /* Move the program break numbytes further */
        sbrk(numbytes);
        /* The new memory will be where the last valid
        * address left off
        */
   
        memory_location 
=  last_valid_address;
        
/*  We''ll move the last valid address forward
        * numbytes
        
*/
        last_valid_address 
=  last_valid_address  +  numbytes;
        
/*  We need to initialize the mem_control_block  */
        current_location_mcb 
=  memory_location;
        current_location_mcb
-> is_available  =   0 ;
        current_location_mcb
-> size  =  numbytes;
    }
    
/*  Now, no matter what (well, except for error conditions),
    * memory_location has the address of the memory, including
    * the mem_control_block
    
*/
    
/*  Move the pointer past the mem_control_block  */
    memory_location 
=  memory_location  +   sizeof ( struct  mem_control_block);
    
/*  Return the pointer  */
    
return  memory_location;
 }

這就是咱們的內存管理器。如今,咱們只須要構建它,並在程序中使用它便可.屢次調用malloc()後空閒內存被切成不少的小內存片斷,這就使得用戶在申請內存使用時,因爲找不到足夠大的內存空間,malloc()須要進行內存整理,使得函數的性能愈來愈低。聰明的程序員經過老是分配大小爲2的冪的內存塊,而最大限度地下降潛在的malloc性能喪失。也就是說,所分配的內存塊大小爲4字節、8字節、16字節、18446744073709551616字節,等等。這樣作最大限度地減小了進入空閒鏈的怪異片斷(各類尺寸的小片斷都有)的數量。儘管看起來這好像浪費了空間,但也容易看出浪費的空間永遠不會超過50%。程序員

相關文章
相關標籤/搜索