句柄:实际就是数组某一元素的索引号。
uC/GUI动态内存分配过程:
1. 最初,假如进行4次内存申请操作:
(1)最初申请的内存,只要没有进行释放操作,那么所申请的内存块一定都是连续的。
(2)数组中第0个元素始终被系统占用,用于维护链表头结点。所以内存分配必须从索引1开始。
2. 释放掉第2块内存后:
释放内存的操作很简单:
(1)从数组的角度:将该控制块中的Size参数清零,即标记为空闲控制块。
(2)从链表的角度:将该节点从双链表中删除;
3. 申请了一块更大的内存:
(1)从数组的角度,查找空闲存储控制块,即Size参数为0的控制块;
(2)从链表的角度,查找相邻节点之间是否有符合条件的空闲内存;
(3)将(1)找到的控制块插入到(2)中合适的位置。
(4)填充存储控制块的相关信息,并返回数组索引号。
4. 又申请了一块较小的内存:
(1)从数组的角度,查找空闲存储控制块,即Size参数为0的控制块
(2)从链表的角度,查找相邻节点之间是否有符合条件的空闲内存;
(3)将(1)找到的控制块插入到(2)中合适的位置。
(4)填充存储控制块的相关信息,并返回数组索引号。
所以,被占用内存块的排列顺序是与控制块链表的顺序一一对应的。即链表中的Off参数是从低到高排列的,而数组中的Off参数是无序的。
关键函数伪代码
分配函数:_Alloc
_Alloc()
{
(1)调用Size2LegalSize将要分配内存大小调整至最小粒度对齐;
(2)从数组的角度,查找空闲存储控制块,在FindFreeHandle中完成;
(3)从链表的角度,查找相邻节点之间是否有符合条件的空闲内存,在FindHole中完成;
(4)将找到的存储控制块插入链表中合适的位置;
(5)填充控制块相关参数,并将分配的内存块清零;
(6)更新GUI_ALLOC的统计参数;
(7)返回控制块索引号。
}
查找空闲存储控制块函数:FindFreeHandle
FindFreeHandle()
{
(1)从控制块数组索引1开始,查找Size为0的元素;
(2)找到了,返回索引号;没找到,返回0。
}
查找空闲内存函数:FindHole
FindHole()
{
(1)从链表头开始,顺序查找相邻链表之间是否有空闲内存,查找方法:
后一结点Off - (前一结点Off + Size)
看后一结点与前一节点之间的间隙是否满足此次分配;
(2)如果(1)找到了符合条件的节点,则返回前一节点的句柄;
如果没找到,则从最后剩余空间开始分配,返回最后一个节点的句柄。
}
释放函数:GUI_ALLOC_Free
GUI_ALLOC_Free()
{
(1)从数组的角度,根据指定的内存句柄,将对应的控制块Size参数清零;
(2)从链表的角度,将该节点从链表中删除;
(3)更新GUI_ALLOC中的统计参数。
}
参考资料:
UCGUI 技术文集:《UCGUI的动态内存分配的原理深入分析》
《几种动态内存分配策略的比较分析》
gliethttp博客:浅析μC/GUI-v3.98之GUI_ALLOC_AllocZero()内存申请转句柄hWin函数
联系客服