本文实现了一个动态内存管理系统,它可以进行内存块的分配与回收。系统会记录已分配和未分配的内存块信息,并采用内存块合并算法来减少内存碎片(内存管理思路来源于FreeRTOS)。
具体来说,系统实现了:
-
内存块的动态分配 - 根据应用的请求,从未分配内存块中选择一个块进行分配。
-
内存块的释放回收 - 当块不再需要时,将其释放并标记为未分配状态。
-
已分配与未分配块记录 - 使用链表或数组等数据结构实时跟踪各状态块的信息。
-
内存块合并 - 在释放块后,检查相邻块状态,若均未分配则合并为一个大块减少碎片。
-
计算剩余内存量 - 随时统计并显示系统未分配内存块的总量。
内存合并的思路
前合并的情况:
(START) -> (内存块A, size=5) -> (内存块B, size=3)
插入内存块C(size=2),发现C紧邻A,且A在前,则:
(START) -> (内存块A+C, size=5+2=7) -> (内存块B, size=3)
后合并的情况:
(START) -> (内存块A, size=5) -> (内存块B, size=3) -> (内存块C, size=2)
插入内存块D(size=3),发现D紧邻B,且B在后,则:
(START) -> (内存块A, size=5) -> (内存块D+B, size=3+3=6) -> (内存块C, size=2)
不合并的情况:
(START) -> (内存块A, size=5) -> (内存块B, size=3) -> (内存块C, size=2)
插入内存块D(size=1),D在A和B之间且都不相连,则:
(START) -> (内存块A, size=5) -> (内存块D, size=1) -> (内存块B, size=3) -> (内存块C, size=2)
实现
定义内存块,由下一块内存块指针、内存块大小、内存分配标志位组成。
- 下一块内存块指针:用于实现链式内存块存储,表示下一个内存块的地址。
- 内存块大小:记录该内存块的大小。
- 内存分配标志位:使用1比特来表示内存块当前的状态,0表示未分配,1表示已分配。
创建起始内存块
创建内存块大小为0的起始块,用于链接后续内存块。
#define MR_HEAP_BLOCK_FREE (0)
#define MR_HEAP_BLOCK_ALLOCATED (1)
#define MR_HEAP_BLOCK_MIN_SIZE (sizeof(struct mr_heap_block) << 1)
static struct mr_heap_block
{
struct mr_heap_block *next;
uint32_t size: 31;
uint32_t allocated: 1;
} heap_start = {MR_NULL, 0, MR_HEAP_BLOCK_FREE};
创建内存数组
创建静态数组,用来作为内存管理分配的内存。
#ifndef MR_CFG_HEAP_SIZE
#define MR_CFG_HEAP_SIZE (4 * 1024) /* If not defined, use 4KB */
#endif /* MR_CFG_HEAP_SIZE */
static uint8_t heap_mem[MR_CFG_HEAP_SIZE] = {0};
初始化内存管理
为内存初始化内存块,将整块内存作为单个内存块。
int mr_heap_init(void)
{
struct mr_heap_block *first_block = (struct mr_heap_block *)&heap_mem;
/* 初始化内存块(消耗一个 sizeof(struct mr_heap_block)) */
first_block->next = MR_NULL;
first_block->size = sizeof(heap_mem) - sizeof(struct mr_heap_block);
first_block->allocated = MR_HEAP_BLOCK_FREE;
/* 初始化起始内存块,启动内存管理 */
heap_start.next = first_block;
return MR_EOK;
}
分配内存
void *mr_malloc(size_t size)
{
struct mr_heap_block *block_prev = &heap_start;
struct mr_heap_block *block = block_prev->next;
void *memory = MR_NULL;
size_t residual = 0;
/* 检查需要申请的内存过小、内存过大,以及内存管理器中还有无内存 */
if ((size == 0) || (size > (UINT32_MAX >> 1) || (block == MR_NULL)))
{
return MR_NULL;
}
/* 字节向上做4对齐 */
size = mr_align4_up(size);
/* 找到符合内存分配大小的内存块 */
while (block->size < size)
{
if (block->next == MR_NULL)
{
return MR_NULL;
}
/* 脱离合理的内存块 */
block_prev = block;
block = block->next;
}
/* 断开内存块链接 */
block_prev->next = block->next;
/* 生成新的内存块并返回内存 */
memory = (void *)((uint8_t *)block) + sizeof(struct mr_heap_block);
/* 剩余内存大小*/
residual = block->size - size;
/* 设置被分配的内存块 */
block->size = size;
block->next = MR_NULL;
block->allocated = MR_HEAP_BLOCK_ALLOCATED;
/* 检测是否够空间生成新的内存块 (MR_HEAP_BLOCK_MIN_SIZE)左移两位等于2倍,需要有大于2个内存块大小,才生成新的内存块 */
if (residual > MR_HEAP_BLOCK_MIN_SIZE)
{
struct mr_heap_block *new_block = (struct mr_heap_block *)(((uint8_t *)memory) + size);
/* 设置新内存块 */
new_block->size = residual - sizeof(struct mr_heap_block);
new_block->next = MR_NULL;
new_block->allocated = MR_HEAP_BLOCK_FREE;
/* 将内存块插入到内存块链表中 */
heap_insert_block(new_block);
}
return memory;
}
释放内存
void mr_free(void *memory)
{
/* 判断内存是否为有效 */
if (memory != MR_NULL)
{
struct mr_heap_block *block = (struct mr_heap_block *)((uint8_t *)memory - sizeof(struct mr_heap_block));
/* 检查内存块是否可以释放 */
if (block->allocated == MR_HEAP_BLOCK_ALLOCATED && block->size != 0)
{
block->allocated = MR_HEAP_BLOCK_FREE;
/* 将内存块插入到内存块链表中 */
heap_insert_block(block);
}
}
}
插入内存块
void heap_insert_block(struct mr_heap_block *block)
{
struct mr_heap_block *block_prev = &heap_start;
/* 搜索前一内存块 */
while (((block_prev->next != MR_NULL) && ((uint32_t)block_prev->next < (uint32_t)block)))
{
block_prev = block_prev->next;
}
if (block_prev->next != MR_NULL)
{
/* 如果前一内存块与需要插入的内存块相连则向前合并 */
if ((void *)(((uint8_t *)block_prev) + sizeof(struct mr_heap_block) + block_prev->size) == (void *)block)
{
block_prev->size += block->size + sizeof(struct mr_heap_block);
block = block_prev;
}
/* 如果需要插入的内存块与后一内存块于相连则向后合并 */
if ((void *)(((uint8_t *)block) + sizeof(struct mr_heap_block) + block->size) == (void *)block_prev->next)
{
block->size += block_prev->next->size + sizeof(struct mr_heap_block);
block->next = block_prev->next->next;
/* 判断当前内存块是否插入*/
if (block != block_prev)
{
block_prev->next = block;
block = block_prev;
}
}
}
/* 若内存块未插入,则插入内存块 */
if (block != block_prev)
{
block->next = block_prev->next;
block_prev->next = block;
}
}