rt-threadの小メモリ管理アルゴリズムの分析

17235 ワード

rt-threadの小メモリ管理は、rt-threadオペレーティングシステムのデフォルトヒープメモリ管理アルゴリズムであり、利用可能なメモリがあるときは、その中から分割されて割り当てられたメモリとして、残りはダイナミックメモリに戻ります.このアルゴリズムは、静的チェーンを使用して実現されます.ソースファイルはルートディレクトリのsrcディレクトリの下にあります.mem.cとmem.hの二つのファイルが含まれています.
1データ構造
小さいメモリ管理アルゴリズムはメモリを一つのメモリブロックと見なします.
struct heap_mem
{
    /* magic and used flag */
    rt_uint16_t magic;     //          ,  0x1ea0,                ,       
    rt_uint16_t used;      //0:   ;1:   

    rt_size_t next, prev;   //     ,     
};
この構造はメモリブロック制御構造の定義である.
2ダイナミックメモリの初期化
/**
 * @ingroup SystemInit
 *
 * This function will init system heap
 *
 * @param begin_addr the beginning address of system page
 * @param end_addr the end address of system page
 */
void rt_system_heap_init(void *begin_addr, void *end_addr)  //                   ,              
{
    struct heap_mem *mem;
    rt_uint32_t begin_align = RT_ALIGN((rt_uint32_t)begin_addr, RT_ALIGN_SIZE);     //             
    rt_uint32_t end_align = RT_ALIGN_DOWN((rt_uint32_t)end_addr, RT_ALIGN_SIZE);  //             

    RT_DEBUG_NOT_IN_INTERRUPT;          //               

    /* alignment addr */
    if ((end_align > (2 * SIZEOF_STRUCT_MEM)) &&                 //                  2          
        ((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align))
    {
        /* calculate the aligned memory size */
        mem_size_aligned = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM;   //                    
    }
    else
    {
        rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x
", (rt_uint32_t)begin_addr, (rt_uint32_t)end_addr); return; } /* point to begin address of heap */ heap_ptr = (rt_uint8_t *)begin_align; //heap_ptr , RT_DEBUG_LOG(RT_DEBUG_MEM, ("mem init, heap begin address 0x%x, size %d
", (rt_uint32_t)heap_ptr, mem_size_aligned)); /* initialize the start of the heap */ mem = (struct heap_mem *)heap_ptr; // , mem->magic = HEAP_MAGIC; // 0x1ea0 mem->next = mem_size_aligned + SIZEOF_STRUCT_MEM; // mem->prev = 0; // mem->used = 0; // /* initialize the end of the heap */ heap_end = (struct heap_mem *)&heap_ptr[mem->next]; // heap_end->magic = HEAP_MAGIC; heap_end->used = 1; // heap_end->next = mem_size_aligned + SIZEOF_STRUCT_MEM; // heap_end->prev = mem_size_aligned + SIZEOF_STRUCT_MEM; // rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); // 1 /* initialize the lowest-free pointer to the start of the heap */ lfree = (struct heap_mem *)heap_ptr; //lfree , , }
上記のコードから分かるように、初期化時に、小メモリ管理アルゴリズムは転送の開始アドレスと末尾アドレスを通じて動的スタックメモリを2つのメモリブロックに初期化し、最初のメモリブロックは動的スタックメモリヘッダアドレスを指し、利用可能な空間は全体の割り当て可能メモリ(2つのメモリ制御ブロック自体の占有サイズを含まない)である.このメモリブロックの次のポインタは末尾メモリ制御ブロックを指します.2番目のメモリブロックは最後尾のメモリ制御ブロックを指します.空き容量は0で、このメモリブロックの前のポインタと後のポインタは自分を指します.
2メモリ割り当て
2.1 mallo c
/**
 * Allocate a block of memory with a minimum of 'size' bytes.
 *
 * @param size is the minimum size of the requested block in bytes.
 *
 * @return pointer to allocated memory or NULL if no free memory was found.
 */
void *rt_malloc(rt_size_t size)      //      
{
    rt_size_t ptr, ptr2;
    struct heap_mem *mem, *mem2;

    RT_DEBUG_NOT_IN_INTERRUPT;     //               

    if (size == 0)           //          0     NULL
        return RT_NULL;

    if (size != RT_ALIGN(size, RT_ALIGN_SIZE))
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d, but align to %d
", size, RT_ALIGN(size, RT_ALIGN_SIZE))); else RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d
", size)); /* alignment size */ size = RT_ALIGN(size, RT_ALIGN_SIZE); // size , , if (size > mem_size_aligned) // , NULL { RT_DEBUG_LOG(RT_DEBUG_MEM, ("no memory
")); return RT_NULL; } /* every data block must be at least MIN_SIZE_ALIGNED long */ // MN_SIZE_ALIGNED, 12 if (size < MIN_SIZE_ALIGNED) size = MIN_SIZE_ALIGNED; /* take memory semaphore */ rt_sem_take(&heap_sem, RT_WAITING_FOREVER); // , 1 , 1, , for (ptr = (rt_uint8_t *)lfree - heap_ptr; // ptr < mem_size_aligned - size; ptr = ((struct heap_mem *)&heap_ptr[ptr])->next) { mem = (struct heap_mem *)&heap_ptr[ptr]; //mem if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) // { /* mem is not used and at least perfect fit is possible: * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= // , (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { /* (in addition to the above, we test if another struct heap_mem (SIZEOF_STRUCT_MEM) containing * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') * -> split large block, create empty remainder, * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, * struct heap_mem would fit in but no data between mem2 and mem2->next * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty * region that couldn't hold data, but when mem->next gets freed, * the 2 regions would be combined, resulting in more free memory */ ptr2 = ptr + SIZEOF_STRUCT_MEM + size; // ,ptr2 /* create mem2 struct */ mem2 = (struct heap_mem *)&heap_ptr[ptr2]; mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; /* and insert it between mem and mem->next */ // mem->next = ptr2; mem->used = 1; // , 1 if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM) // , { ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; } #ifdef RT_MEM_STATS used_mem += (size + SIZEOF_STRUCT_MEM); // , if (max_mem < used_mem) max_mem = used_mem; #endif } else // { /* (a mem2 struct does no fit into the user data space of mem and mem->next will always * be used at this point: if not we have 2 unused structs in a row, plug_holes should have * take care of this). * -> near fit or excact fit: do not split, no mem2 creation * also can't move mem->next directly behind mem, since mem->next * will always be used at this point! */ mem->used = 1; // #ifdef RT_MEM_STATS used_mem += mem->next - ((rt_uint8_t*)mem - heap_ptr); if (max_mem < used_mem) max_mem = used_mem; #endif } /* set memory block magic */ mem->magic = HEAP_MAGIC; if (mem == lfree) // { /* Find next free block after mem and update lowest free pointer */ while (lfree->used && lfree != heap_end) // lfree = (struct heap_mem *)&heap_ptr[lfree->next]; RT_ASSERT(((lfree == heap_end) || (!lfree->used))); // } rt_sem_release(&heap_sem); // , RT_ASSERT((rt_uint32_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_uint32_t)heap_end); RT_ASSERT((rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0); RT_ASSERT((((rt_uint32_t)mem) & (RT_ALIGN_SIZE-1)) == 0); RT_DEBUG_LOG(RT_DEBUG_MEM, ("allocate memory at 0x%x, size: %d
", (rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM), (rt_uint32_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr)))); RT_OBJECT_HOOK_CALL(rt_malloc_hook, (((void *)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM)), size)); /* return the memory data except mem struct */ return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM; // , SIZE_STRUCT_MEM , } } rt_sem_release(&heap_sem); return RT_NULL; // , NULL }
2.2 realloc
/**
 * This function will change the previously allocated memory block.
 *
 * @param rmem pointer to memory allocated by rt_malloc
 * @param newsize the required new size
 *
 * @return the changed memory block address
 */
void *rt_realloc(void *rmem, rt_size_t newsize)
{
    rt_size_t size;
    rt_size_t ptr, ptr2;
    struct heap_mem *mem, *mem2;
    void *nmem;

    RT_DEBUG_NOT_IN_INTERRUPT; //               

    /* alignment size */
    newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);//             
    if (newsize > mem_size_aligned)//      ,     NULL
    {
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("realloc: out of memory
")); return RT_NULL; } /* allocate a new memory block */ if (rmem == RT_NULL)// , return rt_malloc(newsize); rt_sem_take(&heap_sem, RT_WAITING_FOREVER);// if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr || // , (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end) { /* illegal memory */ rt_sem_release(&heap_sem); return rmem; } mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);// ptr = (rt_uint8_t *)mem - heap_ptr; // size = mem->next - ptr - SIZEOF_STRUCT_MEM; // if (size == newsize) // , , { /* the size is the same as */ rt_sem_release(&heap_sem); return rmem; } if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) // , MIN_SIZE { /* split memory block */ #ifdef RT_MEM_STATS used_mem -= (size - newsize); // #endif ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;//ptr2 mem2 = (struct heap_mem *)&heap_ptr[ptr2];// ,mem2 mem2->magic= HEAP_MAGIC;// maigc mem2->used = 0;// mem2->next = mem->next;// next mem2->prev = ptr; mem->next = ptr2;// ptr2 if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM)// { ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2;// } plug_holes(mem2);// rt_sem_release(&heap_sem); return rmem;// } rt_sem_release(&heap_sem); /* expand memory */ nmem = rt_malloc(newsize);// , if (nmem != RT_NULL) /* check memory */ { rt_memcpy(nmem, rmem, size < newsize ? size : newsize); // rt_free(rmem);// } return nmem;// }
注意したいのは、上記のコードの中で、現在のメモリブロックがメモリに余裕がある場合、二つのブロックに分割され、後のブロックが侵害されたら前後のメモリブロックとの統合を試みます.
2.3 caloc
/**
 * This function will contiguously allocate enough space for count objects
 * that are size bytes of memory each and returns a pointer to the allocated
 * memory.
 *
 * The allocated memory is filled with bytes of value zero.
 *
 * @param count number of objects to allocate
 * @param size size of the objects to allocate
 *
 * @return pointer to allocated memory / NULL pointer if there is an error
 */
void *rt_calloc(rt_size_t count, rt_size_t size)
{
    void *p;

    RT_DEBUG_NOT_IN_INTERRUPT;//             

    /* allocate 'count' objects of size 'size' */
    p = rt_malloc(count * size); //    

    /* zero the memory */
    if (p)
        rt_memset(p, 0, count * size);//              0

    return p;
}
3 free
/**
 * This function will release the previously allocated memory block by
 * rt_malloc. The released memory block is taken back to system heap.
 *
 * @param rmem the address of memory which will be released
 */
void rt_free(void *rmem)
{
    struct heap_mem *mem;

    RT_DEBUG_NOT_IN_INTERRUPT;//             

    if (rmem == RT_NULL)
        return;
    RT_ASSERT((((rt_uint32_t)rmem) & (RT_ALIGN_SIZE-1)) == 0);//          
    RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)heap_ptr && //              
              (rt_uint8_t *)rmem < (rt_uint8_t *)heap_end);

    RT_OBJECT_HOOK_CALL(rt_free_hook, (rmem));//      

    if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr ||   //           ,     
        (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end)
    {
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("illegal memory
")); return; } /* Get the corresponding struct heap_mem ... */ mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);// RT_DEBUG_LOG(RT_DEBUG_MEM, ("release memory 0x%x, size: %d
", (rt_uint32_t)rmem, (rt_uint32_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr)))); /* protect the heap from concurrent access */ rt_sem_take(&heap_sem, RT_WAITING_FOREVER); /* ... which has to be in a used state ... */ RT_ASSERT(mem->used); // RT_ASSERT(mem->magic == HEAP_MAGIC);// /* ... and is now unused. */ mem->used = 0; mem->magic = 0; if (mem < lfree)// { /* the newly freed struct is now the lowest */ lfree = mem; } #ifdef RT_MEM_STATS used_mem -= (mem->next - ((rt_uint8_t*)mem - heap_ptr));// #endif /* finally, see if prev or next are free also */ plug_holes(mem); // rt_sem_release(&heap_sem); }
メモリブロックをマージするコードは以下の通りです.
static void plug_holes(struct heap_mem *mem)
{
    struct heap_mem *nmem;
    struct heap_mem *pmem;

    RT_ASSERT((rt_uint8_t *)mem >= heap_ptr);//             
    RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)heap_end);
    RT_ASSERT(mem->used == 0);//         

    /* plug hole forward */     //           
    nmem = (struct heap_mem *)&heap_ptr[mem->next];//       
    if (mem != nmem && //         ,      ,                
        nmem->used == 0 &&
        (rt_uint8_t *)nmem != (rt_uint8_t *)heap_end)
    {
        /* if mem->next is unused and not end of heap_ptr,
         * combine mem and mem->next
         */
        if (lfree == nmem)//             
        {
            lfree = mem;//             
        }
        mem->next = nmem->next;//  
        ((struct heap_mem *)&heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - heap_ptr;
    }

    /* plug hole backward */  //           
    pmem = (struct heap_mem *)&heap_ptr[mem->prev];//       
    if (pmem != mem && pmem->used == 0)//             
    {
        /* if mem->prev is unused, combine mem and mem->prev */
        if (lfree == mem)//            ,         
        {
            lfree = pmem;
        }
        pmem->next = mem->next;//  
        ((struct heap_mem *)&heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - heap_ptr;
    }
}
これで、小メモリアルゴリズムのソースコードの分析が完了しました.上記から分かりました.小メモリ管理アルゴリズムは全体的には、メモリを静的チェーンに初期化して実現しました.初期化する時は二つのメモリブロックしかありません.最初のブロックはメモリブロック制御ブロックを含む以外に、割り当てられる空間も含まれています.この空間はmem_です.size_alignedとは、このアルゴリズムが割り当てられたダイナミックヒープメモリの総サイズであり、割り当てられたメモリはそれよりも大きくできない.さもなければタイムアウトの限界である.第二のブロックはメモリ制御ブロック自体を含み、割り当てられる空間は含まれていません.チェーンの最後としてマークが一定に使用されています.このアルゴリズムには空きのメモリがあります.初期化時は第一の内部ブロックを指します.次にメモリを割り当てます.メモリを割り当てる時は最初に空きメモリから指しているノードからスキャンします.サイズが満足するノードにスキャンしたら、このノードに戻ります.また、このノードが指す空間が十分大きく、十分な空間まで他の空間を割り当てる場合、このノードが指すメモリは2つであり、前のメモリは戻り、後の内部は空きチェーンに格納される.メモリをリリースするとき、アルゴリズムは、リリースされるメモリの前のメモリブロックと後のメモリブロックをチェックし、空きがあればマージします.
これで終わります