一、问题背景
最近在调试ambiq apollo3的蓝牙时,其使用了ARM Cordio WSF的蓝牙协议栈。通过学习wsf_buf.c的实现,看到了一种不同于固定大小内存块的内存池管理方式。它使用了可变大小的内存块分配,支持内存块大小自定义。为了学习其内存管理思想,故特此记录下这两种内存池管理方式的差异。本文将分别介绍了这两种内存池管理方法的实现方式,最后对比两种方式的优缺点和适用场景。
二、两种内存池管理
2.1 固定大小内存块分配(参考正点原子STM32F4 malloc.c)
固定大小内存块的方式,简单的说就是将内存按照相同大小划分成若干个内存块。每一个内存块有一个对应的内存管理表。如果对应的内存管理表为0,则标志未使用。非零表示此内存已经被分配使用中。
分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为 n块,对应的内存管理表,大小也为 n,内存管理表的每一个项对应内存池的一块内存。
内存管理表的项值代表的意义为:当该项值为 0 的时候,代表对应的内存块未被占用,当该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。比如某项值为 10,那么说明包括本项对应的内存块在内,总共分配了 10 个内存块给外部的某个指针。
内寸分配方向如图所示,是从顶->底的分配方向。即首先从最末端开始找空内存。当内存管理刚初始化的时候,内存表全部清零,表示没有任何内存块被占用。
图1 内存块物理上分布图
2.1.1 初始化
在移植这种内存池分配前,我们需要预先定义待分配的内存物理地址和内存块大小。如下所示,在STM32F4 MCU中我们有内部SRAM、内部CCRAM和外部SRAM,共三处内存。通过MEMx_BLOCK_SIZE定义内存块的大小,我们这里将这三处内存都设置为32byte。
MEMx_MAX_SIZE表示内存管理大小。而内存表由于和内存一一对应,故MEMx_MAX_SIZE/MEMx_BLOCK_SIZE就能得到实际的内存表大小(个数)。
//malloc.h 头文件定义:
//mem1内存参数设定.mem1完全处于内部SRAM里面.
#define MEM1_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM1_MAX_SIZE 20*1024 //最大管理内存 100K
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //内存表大小
//mem2内存参数设定.mem2处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!)
#define MEM2_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM2_MAX_SIZE 60 *1024 //最大管理内存60K
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE //内存表大小
//mem3内存参数设定.mem3的内存池处于外部SRAM里面
#define MEM3_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM3_MAX_SIZE 960 *1024 //最大管理内存960K
#define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE/MEM3_BLOCK_SIZE //内存表大小
// malloc.c源码定义
//内存池(32字节对齐)
__align(32) u8 mem1base[MEM1_MAX_SIZE]; //内部SRAM内存池
__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X10000000))); //内部CCM内存池
__align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X68000000))); //外部SRAM内存池
//内存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; //内部SRAM内存池MAP
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM2_MAX_SIZE))); //内部CCM内存池MAP
u16 mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM3_MAX_SIZE))); //外部SRAM内存池MAP
//内存管理参数
const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE};//内存表大小
const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,MEM3_BLOCK_SIZE};//内存分块大小
const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE,MEM3_MAX_SIZE};
初始化时,只用将内存池和内存管理表都清零即可。
//内存管理初始化