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