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