[RT-Thread 源码分析] 1. 内存管理1

rt-thread的小内存管理是其默认的堆内存管理算法。是采用静态链表来实现的,源文件为mem.c。
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);    
        // 得到对齐后的堆内存起始地址  
        // 在rtdef.h中
        // #define RT_ALIGN(size, align)           (((size) + (align) - 1) & ~((align) - 1))
        // 使得begin_align保持四字对齐,这样对于处理器寻址时比较高效
        rt_uint32_t end_align = RT_ALIGN_DOWN((rt_uint32_t)end_addr, RT_ALIGN_SIZE); 
        // 得到对齐后的堆内存末尾地址  
        // #define RT_ALIGN_DOWN(size, align)      ((size) & ~((align) - 1))
        // 使得末尾地址向后四字对齐
        RT_DEBUG_NOT_IN_INTERRUPT;          
        // 确保此函数不是运行在中断例程内  
        // 在rtdebug.h中
        // #define RT_DEBUG_NOT_IN_INTERRUPT                                             \
        //    do                                                                            \
        //    {                                                                             \
        //        rt_base_t level;                                                          \
        //        level = rt_hw_interrupt_disable();                                        \
        //        if (rt_interrupt_get_nest() != 0)                                         \
        //        {                                                                         \
        //            rt_kprintf("Function[%s] shall not used in ISR\n", __FUNCTION__);     \
        //            RT_ASSERT(0)                                                          \
        //        }                                                                         \
        //        rt_hw_interrupt_enable(level);                                            \
        //    }                                                                             \
        //    while (0)
        // 可以判断出是否处于中断嵌套中,保证初始化内存堆不在中断中执行
        /* alignment addr */  
        if ((end_align > (2 * SIZEOF_STRUCT_MEM)) &&                 
            ((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align))  
        // 确保可用动态堆内存大小至少大于可等于2个内存块控制结构大小
        // 在初始化中,分配两个内存控制块,一个指向开始,一个指向末尾
        {  
            /* 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\n",  
                       (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\n",  
                                    (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始终指向最小位置的空闲内存块,这里指向初始地址
    }    

3.分配内存
=====
/**
 * 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)
        return RT_NULL;

    if (size != RT_ALIGN(size, RT_ALIGN_SIZE))
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d, but align to %d\n",
                                    size, RT_ALIGN(size, RT_ALIGN_SIZE)));
    else
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d\n", size));

    /* alignment size */
    size = RT_ALIGN(size, RT_ALIGN_SIZE);
    // 首先将地址对齐

    if (size > mem_size_aligned)
    // 如果地址大于可用地址,则说明内存不够用了
    {
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("no memory\n"));

        return RT_NULL;
    }

    /* every data block must be at least MIN_SIZE_ALIGNED long */
    if (size < MIN_SIZE_ALIGNED)
        size = MIN_SIZE_ALIGNED;
    // 在mem.c中
    // #define MIN_SIZE 12
    // #define MIN_SIZE_ALIGNED     RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE)
    // 分配的内存必须要大于最小要求
    
    /* take memory semaphore */
    rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
    // 获取信号量,和其他内存分配函数保持同步

    for (ptr = (rt_uint8_t *)lfree - heap_ptr;
         ptr < mem_size_aligned - size;
         ptr = ((struct heap_mem *)&heap_ptr[ptr])->next)
        // 从最小位置内存块开始寻找空闲的内存块。ptr指的是相对位置
    {
        mem = (struct heap_mem *)&heap_ptr[ptr];
        // 指向遍历得到的内存块,然后进行判断是否可用和大小是否满足

        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指向了后一个内存块
                mem2       = (struct heap_mem *)&heap_ptr[ptr2];
                mem2->used = 0;
                mem2->next = mem->next;
                mem2->prev = ptr;
                // 这里实现的是一个链表的插入。m
                /* and insert it between mem and mem->next */
                // 插入完成之后,mem为新分配的那个内存块
                // mem2为新的空闲内存块
                mem->next = ptr2;
                mem->used = 1;

                if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM)
                // 如果后一个内存块mem2不是末尾,则让其mem2的prev指向这个空闲内存块
                {
                    ((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 */
                // 寻找下一个空闲内存块,并更新lfree的值,使其指向最小位置内存块
                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\n", 
                          (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;
            // 返回内存块地址,不包括内存控制块
        }
    }

    rt_sem_release(&heap_sem);
    // 都不符合,返回RT_NULL
    return RT_NULL;
}
    


   

posted @ 2013-04-11 09:45  lyyyuna  阅读(576)  评论(0编辑  收藏  举报