uCgui中动态内存管理方法是可配置的,当用户有自己的动态内存管理方式时,需要定义GUI_ALLOC_ALLOC 等宏,如果未定义 GUI_ALLOC_ALLOC 等宏,
uCgui将采用自己的一套“句柄ó指针”的动态内存管理方法,下面将通过uCgui的动态内存管理代码来分析其动态内存管理方法。
uCgui 中的动态内存管理主要提供:分配、释放、碎片整理、使用情况统计等基本功能。
一、uCgui 中动态内存管理方法配置和结构
1、如果用户定义了GUI_ALLOC_ALLOC,则uCgui将采用用户自定义的动态内存管理方式,同时用户还需要定义一下宏:
GUI_ALLOC_H2P (句柄转换为指针)
GUI_ALLOC_FREE (释放句柄对应的内存)
GUI_ALLOC_GETMAXSIZE (得到最大块的大小)
GUI_ALLOC_INIT(可选) (内存管理初始化)
2、当未定义GUI_ALLOC_ALLOC 宏时,uCgui采用自己的动态内存管理方式,实现了如下函数:
GUI_ALLOC_Init
GUI_ALLOC_AllocNoInit
GUI_ALLOC_h2p
GUI_ALLOC_GetNumFreeBytes
GUI_ALLOC_GetMaxSize
3、两种方式共用的函数
GUI_ALLOC_Free
GUI_ALLOC_FreePtr
GUI_ALLOC_GetSize
4、uCgui 动态内存管理模块中用到的宏和数据结构说明
宏:
GUI_ALLOC_SIZE 内存块的总大小,不能为0
GUI_ALLOC_AUTDEFRAG 自动进行碎片整理的开关宏
GUI_BLOCK_ALIGN 对齐单位 1 两字节对齐,2 四字节对齐
GUI_MAXBLOCKS 最大块数(句柄数)
数据结构:
typedef union {
int aintHeap[GUI_ALLOC_SIZE / 4];
U8 abHeap[GUI_ALLOC_SIZE];
} GUI_HEAP;
这是uCgui动态内存管理的堆联合结构,按abHeap访问是按字节对齐的,按aintHeap访问是4字节对齐的。
typedef struct {
GUI_ALLOC_DATATYPE Off; /* 该块内存在堆中的偏移 */
GUI_ALLOC_DATATYPE Size; /* 该块内存的大小 */
HANDLE Next; /* 在已分配内存链表中的下一个句柄号 */
HANDLE Prev; /* 在已分配内存链表中的上一个句柄号 */
} tBlock;
上面这个结构是,uCgui中内存动态分配的句柄结构,没申请一块内存则为其分配一个句柄,所有已经分配的内存句柄构成一个双向链表。
GUI_ALLOC_DATATYPE 宏定义如下:
#if GUI_ALLOC_SIZE <32767
#define GUI_ALLOC_DATATYPE I16
#define GUI_ALLOC_DATATYPE_U U16
#else
#define GUI_ALLOC_DATATYPE I32
#define GUI_ALLOC_DATATYPE_U U32
#endif
HANDLE宏定义如下:
#if GUI_MAXBLOCKS >= 256
#define HANDLE U16
#else
#define HANDLE U8
#endif
堆、句柄数组、内存使用情况数据定义:
struct {
int NumUsedBlocks, NumFreeBlocks, NumFreeBlocksMin; /* For statistical purposes only */
GUI_ALLOC_DATATYPE NumUsedBytes, NumFreeBytes, NumFreeBytesMin;
} GUI_ALLOC;
GUI_HEAP GUI_Heap /* uCgui 动态内存管理的堆 */
static tBlock aBlock[GUI_MAXBLOCKS]; /* uCgui 动态内存管理的句柄数组 */
由此可见,uCgui 的动态内存管理实际上是在编译阶段,分配一个数组,然后在这个数组之上进行内存的申请和释放动作。
二、uCgui 中动态内存管理函数功能说明
1、动态内存管理初始化函数
void GUI_ALLOC_Init(void)
动作:
1、初始化内存使用情况 GUI_ALLOC 的各个值
2、初始化 aBlock[0],如下:
3、设置已经初始化标志 IsInitialized =1;
注释:之所以初始化 aBlock[0],是因为aBlock[0]充当已经分配内存句柄链表的表头,在系统整个运行期间是不会改变的。系统中第一个分配的内存块的句柄号是1
2、动态内存分配函数
GUI_HMEM GUI_ALLOC_AllocNoInit(GUI_ALLOC_DATATYPE Size)
动作:
1、Size 合理性检查
2、加锁
3、分配内存
A、检查内存管理是否已经初始化,未初始化则调用
GUI_ALLOC_Init进行初始化
B、对分配大小 Size进行对齐处理
C、检查系统是否有足够的ram供分配
D、分配空闲的句柄
E、遍历链表,找到合适的堆空间,如果没有则根据
GUI_ALLOC_AUTDEFRAG 确定是否进行碎片整理回收
F、根据得到的堆的空间,设置句柄结构的Size,Off
G.将句柄添加到句柄链表上,句柄在链表上的位置是由 “E”遍历的到得的
H、调整堆使用情况信息
4、解锁
5、返回句柄
注:成功返回句柄号,失败返回0(0号句柄是表头,不参与分配)
3、句柄到指针的转换函数
void* GUI_ALLOC_h2p(GUI_HMEM hMem)
根据句柄号得到在堆上的偏移,结合堆的起始地址将句柄号转换为指针
4、释放句柄对应的内存函数
void GUI_ALLOC_Free(GUI_HMEM hMem)
void GUI_ALLOC_FreePtr(GUI_HMEM *ph)
1、将句柄号对应的句柄结构大小设置为0
2、将句柄结构从链表中删除
3、调整内存分配情况参数
4、扩展的内存分配函数
GUI_ALLOC_AllocInit 分配并初始化
GUI_ALLOC_AllocZero 分配并清零
三、需要注意的地方时,
1、分配到内存后得到的是句柄,需要转换成指针后才能使用
2、该方法在碎片整理回收时,会将内存的内容向前搬移,以将空闲内存相互移动形成连续的大片内存。
四、示意图