内存管理介绍
内存管理实际上就是指管理SRAM.
内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如
何高效、快速的分配,并且在适当的时候释放和回收内存资源。内存管理的实现方法有很多种,其实最终都是要实现两个函数:malloc 和 free。malloc 函数用来内存申请,free 函数用于内存释放。
本章,我们介绍一种比较简单的办法来实现:分块式内存管理。下面我们介绍一下该方法
的实现原理,如下图 所示:
从上图可以看出,分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为了
n 块,对应的内存管理表,大小也为 n,内存管理表的每一个项对应内存池的一块内存。
内存管理表的项值代表的意义为:当该项值为 0 的时候,代表对应的内存块未被占用,当
该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。
比如某项值为 10,那么说明包括本项对应的内存块在内,总共分配了 10 个内存块给外部的某个指针。内存分配方向如上图所示,是从顶→底的分配方向。即首先从最末端开始找空内存。当内存管理刚初始化的时候,内存表全部清零,表示没有任何内存块被占用。
分配原理
当指针 p 调用 malloc 申请内存的时候,先判断 p 要分配的内存块数(m),然后从第 n 开始,向下查找,直到找到 m 块连续的空内存块(即对应内存管理表项为 0),然后将这 m 个内存管理表项的值都设置为 m(标记被占用),最后,把最后的这个空内存块的地址返回指针 p,完成一次分配。注意:如果当内存不够的时候(找到最后也没有找到连续 m 块空闲内存),则返回 NULL 给 p,表示分配失败
释放原理
当 p 申请的内存用完,需要释放的时候,调用 free 函数实现。free 函数先判断 p 指向的内存地址所对应的内存块,然后找到对应的内存管理表项目,得到 p 所占用的内存块数目 m(内存管理表项目的值就是所分配内存块的数目),将这 m 个内存管理表项目的值都清零,标记释放,完成一次内存释放。
核心代码
malloc.h中比较重要的代码
/* mem1 内存参数设定.mem1 是 F103 内部的 SRAM. */
#define MEM1_BLOCK_SIZE 32
/* 内存块大小为 32 字节 */
701
正点原子精英 STM32F103 开发板教程
STM32F103 开发指南
#define MEM1_MAX_SIZE 40 * 1024 /* 最大管理内存 40K, F103ZE 内部 SRAM 总共 512KB */
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE /* 内存表大小 */
/* mem2 内存参数设定.mem3 是 F103 外扩 SRAM */
#define MEM2_BLOCK_SIZE
32
/* 内存块大小为 32 字节 */
#define MEM2_MAX_SIZE 1 * 32
/* 精英板没有外扩内存,故设置一个最小值 */
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE /* 内存表大小 */
/* 内存管理控制器 */
struct _m_mallco_dev
{
void (*init)(uint8_t);
/* 初始化 */
uint16_t (*perused)(uint8_t); /* 内存使用率 */
uint8_t *membase[SRAMBANK]; /* 内存池 管理 SRAMBANK 个区域的内存 */
MT_TYPE *memmap[SRAMBANK]; /* 内存管理状态表 */
uint8_t memrdy[SRAMBANK]; /* 内存管理是否就绪 */
};
根据上面所列的宏定义,那么此时总的内存池分配的大小size(内存池+内存分配管理表):
size = MEM1_MAX_SIZE + (MEM1_MAX_SIZE / MEM1_BLOCK_SIZE) * sizeof(MT_TYPE)
式子可变形为:
MEM1_MAX_SIZE = (MEM1_BLOCK_SIZE * size) / (MEM1_BLOCK_SIZE + sizeof(MT_TYPE))
假设一款单片机的SRAM为64KB,那么我们应该选取合理的大小作为内存池分配的大小。按上述的malloc.h中配置,可以算出
size= 40 * 1024 + (40 * 1024 / 32) * 2 = 43520 = 42.5KB
看以到42.5KB<64KB,这是为了留有一定的空间存储其他全局变量 / 数组
malloc函数
/**
* @brief 内存分配(内部调用)
* @param memx : 所属内存块
* @param size : 要分配的内存大小(字节)
* @retval 内存偏移地址
* @arg 0 ~ 0XFFFFFFFE : 有效的内存偏移地址
* @arg 0XFFFFFFFF : 无效的内存偏移地址
*/
static uint32_t my_mem_malloc(uint8_t memx, uint32_t size)
{
signed long offset = 0;
uint32_t nmemb; /* 需要的内存块数 */
uint32_t cmemb = 0; /* 连续空内存块数 */
uint32_t i;
if (!mallco_dev.memrdy[memx])
{
mallco_dev.init(memx); /* 未初始化,先执行初始化 */
}
if (size == 0) return 0XFFFFFFFF; /* 不需要分配 */
nmemb = size / memblksize[memx]; /* 获取需要分配的连续内存块数 */
if (size % memblksize[memx]) nmemb++;
for (offset = memtblsize[memx] - 1; offset >= 0; offset--) /* 搜索整个内存控制区 */
{
if (!mallco_dev.memmap[memx][offset])
{
cmemb++; /* 连续空内存块数增加 */
}
else
{
cmemb = 0; /* 连续内存块清零 */
}
if (cmemb == nmemb) /* 找到了连续nmemb个空内存块 */
{
for (i = 0; i < nmemb; i++) /* 标注内存块非空 */
{
mallco_dev.memmap[memx][offset + i] = nmemb;
}
return (offset * memblksize[memx]); /* 返回偏移地址 */
}
}
return 0XFFFFFFFF; /* 未找到符合分配条件的内存块 */
}
/**
* @brief 分配内存(外部调用)
* @param memx : 所属内存块
* @param size : 要分配的内存大小(字节)
* @retval 分配到的内存首地址.
*/
void *mymalloc(uint8_t memx, uint32_t size)
{
uint32_t offset;
offset = my_mem_malloc(memx, size);
if (offset == 0XFFFFFFFF) /* 申请出错 */
{
return NULL; /* 返回空(0) */
}
else /* 申请没问题, 返回首地址 */
{
return (void *)((uint32_t)mallco_dev.membase[memx] + offset);
}
}
free函数
/**
* @brief 释放内存(内部调用)
* @param memx : 所属内存块
* @param offset : 内存地址偏移
* @retval 释放结果
* @arg 0, 释放成功;
* @arg 1, 释放失败;
* @arg 2, 超区域了(失败);
*/
static uint8_t my_mem_free(uint8_t memx, uint32_t offset)
{
int i;
if (!mallco_dev.memrdy[memx]) /* 未初始化,先执行初始化 */
{
mallco_dev.init(memx);
return 1; /* 未初始化 */
}
if (offset < memsize[memx]) /* 偏移在内存池内. */
{
int index = offset / memblksize[memx]; /* 偏移所在内存块号码 */
int nmemb = mallco_dev.memmap[memx][index]; /* 内存块数量 */
for (i = 0; i < nmemb; i++) /* 内存块清零 */
{
mallco_dev.memmap[memx][index + i] = 0;
}
return 0;
}
else
{
return 2; /* 偏移超区了. */
}
}
/**
* @brief 释放内存(外部调用)
* @param memx : 所属内存块
* @param ptr : 内存首地址
* @retval 无
*/
void myfree(uint8_t memx, void *ptr)
{
uint32_t offset;
if (ptr == NULL)return; /* 地址为0. */
offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];
my_mem_free(memx, offset); /* 释放内存 */
}