摘要:本文为大家分析LiteOS动静内存模块bestfit算法的源代码,蕴含动态内存的构造体、动态内存池初始化、动态内存申请、开释等。
本文分享自华为云社区《LiteOS内核源码剖析系列十三 动态内存Bestfit调配算法》,原文作者:zhushy 。
内存治理模块管理系统的内存资源,它是操作系统的外围模块之一,次要包含内存的初始化、调配以及开释。
在零碎运行过程中,内存治理模块通过对内存的申请/开释来治理用户和OS对内存的应用,使内存的利用率和应用效率达到最优,同时最大限度地解决零碎的内存碎片问题。
Huawei LiteOS的内存治理分为动态内存治理和动态内存治理,提供内存初始化、调配、开释等性能。
- 动态内存:在动态内存池中调配用户指定大小的内存块。
(1)长处:按需分配。
(2)毛病:内存池中可能呈现碎片。
- 动态内存:在动态内存池中调配用户初始化时预设(固定)大小的内存块。
(1)长处:调配和开释效率高,动态内存池中无碎片。
(2)毛病:只能申请到初始化预设大小的内存块,不能按需申请。
上一系列剖析了动态内存,咱们开始剖析动态内存。动态内存治理次要用于用户须要应用大小不等的内存块的场景。当用户须要应用内存时,能够通过操作系统的动态内存申请函数索取指定大小的内存块,一旦应用结束,通过动态内存开释函数偿还所占用内存,使之能够重复使用。
LiteOS动态内存反对bestfit(也称为dlink)和bestfit_little两种内存治理算法。本文次要剖析LiteOS动态内存的bestfit算法,后续系列会持续剖析动态内存的bestfit_little的算法。
本文通过剖析LiteOS动静内存模块的源码,帮忙读者把握动态内存的应用。LiteOS动静内存模块的源代码,均能够在LiteOS开源站点https://gitee.com/LiteOS/LiteOS 获取。动态内存bestfit算法的源代码、开发文档,示例程序代码如下:
- LiteOS内核动态内存源代码
包含动态内存的bestfit算法公有头文件kernel\base\mem\bestfit\los_memory_internal.h、动态内存公有头文件kernel\base\include\los_memory_pri.h、内存头文件kernel\include\los_memory.h、多链表头文件kernel\base\include\los_multipledlinkhead_pri.h、C源代码文件kernel\base\mem\bestfit\los_memory.c、C源代码文件kernel\base\mem\bestfit\los_multipledlinkhead.c。
- 开发指南文档–内存
在线文档https://gitee.com/LiteOS/Lite...
接下来,咱们看下动态内存的构造体,动态内存初始化,动态内存罕用操作的源代码。
1、动态内存构造体定义和罕用宏定义
1.1 动态内存构造体定义
动态内存bestfit算法的构造体有动态内存池信息结构体LosMemPoolInfo,多双向链表表头构造体LosMultipleDlinkHead、动态内存链表节点构造体LosMemDynNode,内存链表管制节点构造体LosMemCtlNode。
对动态内存应用如下示意图进行阐明,对一块动态内存区域,第一局部是内存池信息结构体LosMemPoolInfo,接着是第二局部多双向链表表头构造体LosMultipleDlinkHead,第三局部是动态内存链表节点构造体LosMemDynNode,内存链表管制节点构造体LosMemCtlNode。应用动态内存的bestfit算法初始化后,第三局部蕴含2个内存块,第一个内存块蕴含内存链表管制节点构造体LosMemCtlNode和内存块数据区,尾节点只蕴含内存链表管制节点构造体LosMemCtlNode,没有数据区。管制节点构造体LosMemCtlNode持有上一个内存块的指针。有数据区的内存块会挂载在第二局部的链表上。当申请内存时,依据须要内存的大小,从第二局部的链表获取适合的内存块,如果内存块超出需要,会进行切割,残余的局部和后续的闲暇节点合并,从新挂载到第二局部的链表上。
1.1.1 动态内存池信息结构体LosMemPoolInfo
在文件kernel\base\include\los_memory_pri.h中,定义了内存池信息结构体LosMemPoolInfo。这是动态内存池的第一局部,保护内存池的开始地址和大小信息。动态内存bestfit算法和bestfit_little算法中都定义了该构造体,构造体名称一样,成员有差别,咱们先看看bestfit算法的构造体,源代码如下。两个次要的成员是内存池开始地址.pool和内存池大小.poolSize。其余构造体须要开启相应的宏才失效,暂不探讨这些宏相干的个性。
typedef struct { VOID *pool; /* 内存池的内存开始地址 */ UINT32 poolSize; /* 内存池大小 */#ifdef LOSCFG_MEM_TASK_STAT Memstat stat;#endif#ifdef LOSCFG_MEM_MUL_POOL VOID *nextPool;#endif#ifdef LOSCFG_KERNEL_MEM_SLAB_EXTENTION struct LosSlabControlHeader slabCtrlHdr;#endif} LosMemPoolInfo;
1.1.2 多双向链表表头构造体LosMultipleDlinkHead
在文件kernel\base\include\los_multipledlinkhead_pri.h中,定义了内存池多双向链表表头构造体LosMultipleDlinkHead。这是动态内存池的第二局部,构造体自身是一个数组,每个元素是一个双向链表,所有free节点的管制头都会被分类挂在这个数组的双向链表中。
假如内存容许的最小节点为2^min字节,则数组的第一个双向链表存储的是所有size为2^min<size< 2^(min+1)的free节点,第二个双向链表存储的是所有size为2^(min+1)<size< 2^(min+2)的free节点,顺次类推第n个双向链表存储的是所有size为2^(min+n-1)<size< 2^(min+n)的free节点。每次申请内存的时候,会从这个数组检索最合适大小的free节点以分配内存。每次开释内存时,会将该内存作为free节点存储至这个数组以便下次再应用。
构造体源代码如下,非常简单,是一个长度为OS_MULTI_DLNK_NUM的双向链表数组。
typedef struct { LOS_DL_LIST listHead[OS_MULTI_DLNK_NUM];} LosMultipleDlinkHead;
咱们再看看和构造体LosMultipleDlinkHead相干的宏定义,OS_MIN_MULTI_DLNK_LOG2和OS_MAX_MULTI_DLNK_LOG2指定了双向链表中存储的内存节点的大小拜访,第一个存储大小在[2^4,2^5)的闲暇内存节点,顺次类推,第26个即OS_MULTI_DLNK_NUM存储大小在[2^29,2^30)的闲暇内存节点。多链表表头构造体占用的内存大小为OS_DLNK_HEAD_SIZE。
#define OS_MAX_MULTI_DLNK_LOG2 29#define OS_MIN_MULTI_DLNK_LOG2 4#define OS_MULTI_DLNK_NUM ((OS_MAX_MULTI_DLNK_LOG2 - OS_MIN_MULTI_DLNK_LOG2) + 1)#define OS_DLNK_HEAD_SIZE OS_MULTI_DLNK_HEAD_SIZE#define OS_MULTI_DLNK_HEAD_SIZE sizeof(LosMultipleDlinkHead)
1.1.3 动态内存链表节点构造体LosMemDynNode和链表管制节点构造体LosMemCtlNode
在文件kernel\base\mem\bestfit\los_memory_internal.h中定义2个构造体,动态内存链表节点构造体LosMemDynNode和链表管制节点构造体LosMemCtlNode。这是动态内存池的第三局部,占用内存池极大局部的空间,是用于寄存各节点的理论区域。设计2个构造体的起因为满足备份内存链表节点的须要。能够看出开启备份链表节点的宏LOSCFG_MEM_HEAD_BACKUP时,LosMemDynNode构造体蕴含2个LosMemCtlNode,一个是.backupNode,另外一个是.selfNode。2个构造体源码如下,重要的成员的解释见正文局部。
/* 内存链表管制节点构造体 */typedef struct { union { LOS_DL_LIST freeNodeInfo; /* 闲暇内存链表节点 */ struct { UINT32 magic; /* 魔术字 */ UINT32 taskId : 16; /* 应用内存的工作Id */#ifdef LOSCFG_MEM_MUL_MODULE UINT32 moduleId : 16;#endif }; }; struct tagLosMemDynNode *preNode; /* 指针,指针前一个内存节点 */#ifdef LOSCFG_MEM_HEAD_BACKUP UINT32 gapSize; UINTPTR checksum; /* magic = xor checksum */#endif#ifdef LOSCFG_MEM_RECORDINFO UINT32 originSize;#ifdef LOSCFG_AARCH64 UINT32 reserve1; /* 64-bit alignment */#endif#endif#ifdef LOSCFG_MEM_LEAKCHECK UINTPTR linkReg[LOS_RECORD_LR_CNT];#endif#ifdef LOSCFG_AARCH64 UINT32 reserve2; /* 64-bit alignment */#endif UINT32 sizeAndFlag; /* 大小和标记,高2位用作标记,其余位示意大小 */} LosMemCtlNode;/* 内存链表节点构造体 */typedef struct tagLosMemDynNode {#ifdef LOSCFG_MEM_HEAD_BACKUP LosMemCtlNode backupNode;#endif LosMemCtlNode selfNode;} LosMemDynNode;
1.2 动态内存罕用宏定义
动态内存头文件kernel\base\mem\bestfit\los_memory_internal.h中还提供了一些重要的宏定义,这些宏十分重要,在剖析源代码前须要相熟下这些宏的定义。
⑴处的OS_MEM_ALIGN(p, alignSize)用于对齐内存地址,⑵处OS_MEM_NODE_HEAD_SIZE示意一个内存链表节点的大小,OS_MEM_MIN_POOL_SIZE示意一个动态内存池的最小大小,蕴含一个内存池信息结构体大小,1个多链表表头构造体大小,和2个链表节点大小。⑶处IS_POW_TWO(value)判断value是否是2的幂。⑷处定义内存池地址对齐值,内存节点对齐值。
⑸处定义是否应用、是否对齐2个标记位,别离是高31位,和高30位。而后别离定义3个宏函数,用于获取是否已应用/对齐,设置标记为应用/对齐,获取去除标记后的应用大小。
⑹处OS_MEM_HEAD_ADDR(pool)示意动态内存池第二局部多链表表头构造体的开始地址。宏函数OS_MEM_HEAD(pool, size)用于计算大小为size的内存块对应的多链表表头的地址,其实就是把内存池第三局部的内存块的大小映射到第二局部的链表地位上。其中调用的函数OsDLnkMultiHead()后文咱们再剖析。
⑺处定义内存节点操作相干的宏。OS_MEM_NEXT_NODE(node)获取内存节点的下一个内存节点,OS_MEM_FIRST_NODE(pool)获取内存池中第一个内存节点,OS_MEM_END_NODE(pool, size)获取内存池中最初一个内存节点。
⑻处定义2个宏,判断一个内存地址是否处于指定的区间,两者区别是是否开闭区间。⑼处的2个宏对边界内存节点设置魔术字和魔术字校验。
⑴ #define OS_MEM_ALIGN(p, alignSize) (((UINTPTR)(p) + (alignSize) - 1) & ~((UINTPTR)((alignSize) - 1)))⑵ #define OS_MEM_NODE_HEAD_SIZE sizeof(LosMemDynNode) #define OS_MEM_MIN_POOL_SIZE (OS_DLNK_HEAD_SIZE + (2 * OS_MEM_NODE_HEAD_SIZE) + sizeof(LosMemPoolInfo))⑶ #define IS_POW_TWO(value) ((((UINTPTR)(value)) & ((UINTPTR)(value) - 1)) == 0)⑷ #define POOL_ADDR_ALIGNSIZE 64 #ifdef LOSCFG_AARCH64 #define OS_MEM_ALIGN_SIZE 8 #else #define OS_MEM_ALIGN_SIZE 4 #endif⑸ #define OS_MEM_NODE_USED_FLAG 0x80000000U #define OS_MEM_NODE_ALIGNED_FLAG 0x40000000U #define OS_MEM_NODE_ALIGNED_AND_USED_FLAG (OS_MEM_NODE_USED_FLAG | OS_MEM_NODE_ALIGNED_FLAG) #define OS_MEM_NODE_GET_ALIGNED_FLAG(sizeAndFlag) \ ((sizeAndFlag) & OS_MEM_NODE_ALIGNED_FLAG) #define OS_MEM_NODE_SET_ALIGNED_FLAG(sizeAndFlag) \ ((sizeAndFlag) = ((sizeAndFlag) | OS_MEM_NODE_ALIGNED_FLAG)) #define OS_MEM_NODE_GET_ALIGNED_GAPSIZE(sizeAndFlag) \ ((sizeAndFlag) & ~OS_MEM_NODE_ALIGNED_FLAG) #define OS_MEM_NODE_GET_USED_FLAG(sizeAndFlag) \ ((sizeAndFlag) & OS_MEM_NODE_USED_FLAG) #define OS_MEM_NODE_SET_USED_FLAG(sizeAndFlag) \ ((sizeAndFlag) = ((sizeAndFlag) | OS_MEM_NODE_USED_FLAG)) #define OS_MEM_NODE_GET_SIZE(sizeAndFlag) \ ((sizeAndFlag) & ~OS_MEM_NODE_ALIGNED_AND_USED_FLAG)⑹ #define OS_MEM_HEAD(pool, size) \ OsDLnkMultiHead(OS_MEM_HEAD_ADDR(pool), size) #define OS_MEM_HEAD_ADDR(pool) \ ((VOID *)((UINTPTR)(pool) + sizeof(LosMemPoolInfo)))⑺ #define OS_MEM_NEXT_NODE(node) \ ((LosMemDynNode *)(VOID *)((UINT8 *)(node) + OS_MEM_NODE_GET_SIZE((node)->selfNode.sizeAndFlag))) #define OS_MEM_FIRST_NODE(pool) \ ((LosMemDynNode *)(VOID *)((UINT8 *)OS_MEM_HEAD_ADDR(pool) + OS_DLNK_HEAD_SIZE)) #define OS_MEM_END_NODE(pool, size) \ ((LosMemDynNode *)(VOID *)(((UINT8 *)(pool) + (size)) - OS_MEM_NODE_HEAD_SIZE))⑻ #define OS_MEM_MIDDLE_ADDR_OPEN_END(startAddr, middleAddr, endAddr) \ (((UINT8 *)(startAddr) <= (UINT8 *)(middleAddr)) && ((UINT8 *)(middleAddr) < (UINT8 *)(endAddr))) #define OS_MEM_MIDDLE_ADDR(startAddr, middleAddr, endAddr) \ (((UINT8 *)(startAddr) <= (UINT8 *)(middleAddr)) && ((UINT8 *)(middleAddr) <= (UINT8 *)(endAddr)))⑼ #define OS_MEM_SET_MAGIC(value) \ (value) = (UINT32)((UINTPTR)&(value) ^ (UINTPTR)(-1)) #define OS_MEM_MAGIC_VALID(value) \ (((UINTPTR)(value) ^ (UINTPTR)&(value)) == (UINTPTR)(-1))
咱们看下宏中调用的函数OsDLnkMultiHead(),函数定义在文件kernel\base\mem\bestfit\los_multipledlinkhead.c中。该函数须要2个参数,VOID *headAddr为第二局部的多链表数组的起始地址,UINT32 size为内存块的大小。该函数把内存池第三局部的内存块的大小映射到第二局部的链表地位上,咱们剖析下代码。
⑴处的函数OsLog2()名称中的Log是对数英文logarithm的缩写,函数用于计算以2为底的对数的整数局部,输出参数是内存池第三局部的内存块的大小size,输入是第二局部多链表数组的数组索引,见代码片段⑵。⑶处如果索引大于OS_MAX_MULTI_DLNK_LOG2,无奈调配这么大的内存块,返回NULL。⑷处如果索引小于等于OS_MIN_MULTI_DLNK_LOG2,则应用最小值作为索引。⑸处返回多链表表头中的链表头节点。
STATIC INLINE UINT32 OsLog2(UINT32 size){⑴ return (size > 0) ? (UINT32)LOS_HighBitGet(size) : 0;}LITE_OS_SEC_TEXT_MINOR LOS_DL_LIST *OsDLnkMultiHead(VOID *headAddr, UINT32 size){ LosMultipleDlinkHead *dlinkHead = (LosMultipleDlinkHead *)headAddr;⑵ UINT32 index = OsLog2(size); if (index > OS_MAX_MULTI_DLNK_LOG2) {⑶ return NULL; } else if (index <= OS_MIN_MULTI_DLNK_LOG2) {⑷ index = OS_MIN_MULTI_DLNK_LOG2; }⑸ return dlinkHead->listHead + (index - OS_MIN_MULTI_DLNK_LOG2);}
2、动态内存罕用操作
Huawei LiteOS零碎中的动态内存治理模块为用户提供初始化和删除内存池、申请、开释动态内存等操作,咱们来剖析下接口的源代码。在剖析下内存操作接口之前,咱们先看下一下罕用的外部接口。
2.1 动态内存外部接口
2.1.1 革除内存节点内容
函数VOID OsMemClearNode(LosMemDynNode *node)用于革除给定的内存节点内容,设置内存数据内容为0。代码比较简单,间接调用函数memset_s()实现操作。
STATIC INLINE VOID OsMemClearNode(LosMemDynNode *node){ (VOID)memset_s((VOID *)node, sizeof(LosMemDynNode), 0, sizeof(LosMemDynNode));}
2.1.2 合并内存节点
函数VOID OsMemMergeNode(LosMemDynNode node)用于合并给定节点LosMemDynNode node和它前一个闲暇节点,而后革除给定节点的内容。⑴处把前一个节点的大小加上要合入节点的大小。⑵处获取给定节点的下一个节点,而后把它的前一个节点指向给定节点的前一个节点。⑶处革除给定节点的内容,实现节点的合并。
STATIC INLINE VOID OsMemMergeNode(LosMemDynNode *node){ LosMemDynNode *nextNode = NULL;⑴ node->selfNode.preNode->selfNode.sizeAndFlag += node->selfNode.sizeAndFlag;⑵ nextNode = (LosMemDynNode *)((UINTPTR)node + node->selfNode.sizeAndFlag); nextNode->selfNode.preNode = node->selfNode.preNode;#ifdef LOSCFG_MEM_HEAD_BACKUP OsMemNodeSave(node->selfNode.preNode); OsMemNodeSave(nextNode);#endif⑶ OsMemClearNode(node);}
2.1.3 宰割内存节点
函数VOID OsMemSplitNode(VOID pool, LosMemDynNode allocNode, UINT32 allocSize)用于宰割内存节点,须要三个参数。VOID pool是内存池起始地址,LosMemDynNode allocNode示意从该内存节点调配出须要的内存,UINT32 allocSize是须要调配的内存大小。宰割之后残余的局部,如果下一个节点是闲暇节点,则合并一起。宰割残余的节点会挂载到内存第二局部的多链表上。
⑴处获取动态内存池的第一个内存管制节点,⑵处示意newFreeNode是调配之后残余的闲暇内存节点,设置它的上一个节点为调配的节点,并设置残余内存大小。⑶处获取下一个节点,下一个节点的前一个节点设置为新的闲暇节点newFreeNode。⑷处判断下一个节点是否被应用,如果没有应用,则把下一个节点从链表中删除,而后和闲暇节点newFreeNode合并。⑸处依据闲暇节点newFreeNode的大小获取对应的链表头节点,而后执行⑹把闲暇内存节点挂载到链表上。
STATIC INLINE VOID OsMemSplitNode(VOID *pool, LosMemDynNode *allocNode, UINT32 allocSize){ LosMemDynNode *newFreeNode = NULL; LosMemDynNode *nextNode = NULL; LOS_DL_LIST *listNodeHead = NULL;⑴ const VOID *firstNode = (const VOID *)((UINT8 *)OS_MEM_HEAD_ADDR(pool) + OS_DLNK_HEAD_SIZE);⑵ newFreeNode = (LosMemDynNode *)(VOID *)((UINT8 *)allocNode + allocSize); newFreeNode->selfNode.preNode = allocNode; newFreeNode->selfNode.sizeAndFlag = allocNode->selfNode.sizeAndFlag - allocSize; allocNode->selfNode.sizeAndFlag = allocSize;⑶ nextNode = OS_MEM_NEXT_NODE(newFreeNode); nextNode->selfNode.preNode = newFreeNode;⑷ if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->selfNode.sizeAndFlag)) { OsMemListDelete(&nextNode->selfNode.freeNodeInfo, firstNode); OsMemMergeNode(nextNode); }#ifdef LOSCFG_MEM_HEAD_BACKUP else { OsMemNodeSave(nextNode); }#endif⑸ listNodeHead = OS_MEM_HEAD(pool, newFreeNode->selfNode.sizeAndFlag); OS_CHECK_NULL_RETURN(listNodeHead);⑹ OsMemListAdd(listNodeHead, &newFreeNode->selfNode.freeNodeInfo, firstNode);#ifdef LOSCFG_MEM_HEAD_BACKUP OsMemNodeSave(newFreeNode);#endif}
2.1.4 从新申请内存
OsMemReAllocSmaller()函数用于从一个大的内存块里从新申请一个较小的内存,他须要的4个参数别离是:LosMemPoolInfo pool是内存池起始地址,UINT32 allocSize是从新申请的内存的大小,LosMemDynNode node是以后须要从新分配内存的内存节点,UINT32 nodeSize是以后节点的大小。⑴设置内存节点selfNode.sizeAndFlag为去除标记后的理论大小,⑵按需宰割节点,⑶宰割后的节点设置已应用标记,实现实现申请内存。
STATIC INLINE VOID OsMemReAllocSmaller(LosMemPoolInfo *pool, UINT32 allocSize, LosMemDynNode *node, UINT32 nodeSize){ if ((allocSize + OS_MEM_NODE_HEAD_SIZE + OS_MEM_ALIGN_SIZE) <= nodeSize) {⑴ node->selfNode.sizeAndFlag = nodeSize;⑵ OsMemSplitNode(pool, node, allocSize);⑶ OS_MEM_NODE_SET_USED_FLAG(node->selfNode.sizeAndFlag);#ifdef LOSCFG_MEM_HEAD_BACKUP OsMemNodeSave(node);#endif OS_MEM_REDUCE_USED(&pool->stat, nodeSize - allocSize, OS_MEM_TASKID_GET(node)); }#ifdef LOSCFG_MEM_LEAKCHECK OsMemLinkRegisterRecord(node);#endif}
2.1.5 合并节点从新申请内存
最初,再来看下函数函数OsMemMergeNodeForReAllocBigger(),用于合并内存节点,重新分配更大的内存空间。它须要5个参数,LosMemPoolInfo pool是内存池起始地址,UINT32 allocSize是从新申请的内存的大小,LosMemDynNode node是以后须要从新分配内存的内存节点,UINT32 nodeSize是以后节点的大小,LosMemDynNode *nextNode是下一个内存节点。⑴处获取内存池中第一个内存节点,⑵设置内存节点selfNode.sizeAndFlag为去除标记后的理论大小,⑶把下一个节点从链表上删除,而后合并节点。⑷处如果合并后的节点大小超过须要重新分配的大小,则宰割节点。
STATIC INLINE VOID OsMemMergeNodeForReAllocBigger(LosMemPoolInfo *pool, UINT32 allocSize, LosMemDynNode *node, UINT32 nodeSize, LosMemDynNode *nextNode){⑴ const VOID *firstNode = (const VOID *)((UINT8 *)OS_MEM_HEAD_ADDR(pool) + OS_DLNK_HEAD_SIZE);⑵ node->selfNode.sizeAndFlag = nodeSize;⑶ OsMemListDelete(&nextNode->selfNode.freeNodeInfo, firstNode); OsMemMergeNode(nextNode); if ((allocSize + OS_MEM_NODE_HEAD_SIZE + OS_MEM_ALIGN_SIZE) <= node->selfNode.sizeAndFlag) {⑷ OsMemSplitNode(pool, node, allocSize); } OS_MEM_ADD_USED(&pool->stat, node->selfNode.sizeAndFlag - nodeSize, OS_MEM_TASKID_GET(node)); OS_MEM_NODE_SET_USED_FLAG(node->selfNode.sizeAndFlag);#ifdef LOSCFG_MEM_HEAD_BACKUP OsMemNodeSave(node);#endif#ifdef LOSCFG_MEM_LEAKCHECK OsMemLinkRegisterRecord(node);#endif}
2.2 初始化动态内存池
咱们剖析下初始化动态内存池函数UINT32 LOS_MemInit(VOID pool, UINT32 size)的代码。咱们先看看函数参数,VOID pool是动态内存池的起始地址,UINT32 size是初始化的动态内存池的总大小,size须要小于等于*pool开始的内存区域的大小,否则会影响前面的内存区域,还须要大于动态内存池的最小值OS_MEM_MIN_POOL_SIZE。[pool, pool + size]不能和其余内存池抵触。
咱们看下代码,⑴处对传入参数进行校验,传入参数须要内存对齐。⑵处如果开启多内存池的宏LOSCFG_MEM_MUL_POOL才会执行。⑶处调用函数OsMemInit()进行内存池初始化,这是初始化的内存的外围函数。⑷处开启宏LOSCFG_KERNEL_MEM_SLAB_EXTENTION反对时,才会执行。
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MemInit(VOID *pool, UINT32 size){ UINT32 intSave;⑴ if ((pool == NULL) || (size < OS_MEM_MIN_POOL_SIZE)) { return LOS_NOK; } if (!IS_ALIGNED(size, OS_MEM_ALIGN_SIZE) || !IS_ALIGNED(pool, OS_MEM_ALIGN_SIZE)) { PRINT_WARN("pool [%p, %p) size 0x%x should be aligned with OS_MEM_ALIGN_SIZE\n", pool, (UINTPTR)pool + size, size); size = OS_MEM_ALIGN(size, OS_MEM_ALIGN_SIZE) - OS_MEM_ALIGN_SIZE; } MEM_LOCK(intSave);⑵ if (OsMemMulPoolInit(pool, size)) { MEM_UNLOCK(intSave); return LOS_NOK; }⑶ if (OsMemInit(pool, size) != LOS_OK) { (VOID)OsMemMulPoolDeinit(pool); MEM_UNLOCK(intSave); return LOS_NOK; }⑷ OsSlabMemInit(pool, size); MEM_UNLOCK(intSave); LOS_TRACE(MEM_INFO_REQ, pool); return LOS_OK;}
咱们持续看下函数OsMemInit()。⑴处设置动态内存池信息结构体LosMemPoolInfo的起始地址和大小。⑵处初始化第二局部的多双向链表表头构造体LosMultipleDlinkHead。⑶处获取内存池的第一个内存管制节点,而后设置它的大小为(poolSize - (UINT32)((UINTPTR)newNode - (UINTPTR)pool) - OS_MEM_NODE_HEAD_SIZE),该节点大小等于第三局部减去一个内存管制节点的大小。再设置该内存节点的上一个节点为内存池的最初一个节点OS_MEM_END_NODE(pool, poolSize)。
⑷处依据第一个内存节点的大小获取多链表表头节点listNodeHead,而后把内存节点插入到双向链表中。⑸处获取内存池的尾节点,革除内容而后设置其大小和上一个节点,并设置已应用标记。⑹处对未节点设置魔术字,指定应用该内存块的工作Id。如果开启调测宏LOSCFG_MEM_TASK_STAT、LOSCFG_MEM_HEAD_BACKUP,还会有些其余操作,自行浏览即可。
STATIC UINT32 OsMemInit(VOID *pool, UINT32 size){ LosMemPoolInfo *poolInfo = (LosMemPoolInfo *)pool; LosMemDynNode *newNode = NULL; LosMemDynNode *endNode = NULL; LOS_DL_LIST *listNodeHead = NULL; UINT32 poolSize = OsLmsMemInit(pool, size); if (poolSize == 0) { poolSize = size; }⑴ poolInfo->pool = pool; poolInfo->poolSize = poolSize;⑵ OsDLnkInitMultiHead(OS_MEM_HEAD_ADDR(pool));⑶ newNode = OS_MEM_FIRST_NODE(pool); newNode->selfNode.sizeAndFlag = (poolSize - (UINT32)((UINTPTR)newNode - (UINTPTR)pool) - OS_MEM_NODE_HEAD_SIZE); newNode->selfNode.preNode = (LosMemDynNode *)OS_MEM_END_NODE(pool, poolSize);⑷ listNodeHead = OS_MEM_HEAD(pool, newNode->selfNode.sizeAndFlag); if (listNodeHead == NULL) { return LOS_NOK; } LOS_ListTailInsert(listNodeHead, &(newNode->selfNode.freeNodeInfo));⑸ endNode = (LosMemDynNode *)OS_MEM_END_NODE(pool, poolSize); (VOID)memset_s(endNode, sizeof(*endNode), 0, sizeof(*endNode)); endNode->selfNode.preNode = newNode; endNode->selfNode.sizeAndFlag = OS_MEM_NODE_HEAD_SIZE; OS_MEM_NODE_SET_USED_FLAG(endNode->selfNode.sizeAndFlag);⑹ OsMemSetMagicNumAndTaskID(endNode);#ifdef LOSCFG_MEM_TASK_STAT UINT32 statSize = sizeof(poolInfo->stat); (VOID)memset_s(&poolInfo->stat, statSize, 0, statSize); poolInfo->stat.memTotalUsed = sizeof(LosMemPoolInfo) + OS_MULTI_DLNK_HEAD_SIZE + OS_MEM_NODE_GET_SIZE(endNode->selfNode.sizeAndFlag); poolInfo->stat.memTotalPeak = poolInfo->stat.memTotalUsed;#endif#ifdef LOSCFG_MEM_HEAD_BACKUP OsMemNodeSave(newNode); OsMemNodeSave(endNode);#endif return LOS_OK;}
2.3 申请动态内存
初始化动态内存池后,咱们能够应用函数VOID LOS_MemAlloc(VOID pool, UINT32 size)来申请动态内存,上面剖析下源码。
⑴处对参数进行校验,内存池地址不能为空,申请的内存大小不能为0。⑵处判断申请的内存大小是否已标记为应用或对齐。把下一个可用节点赋值给nodeTmp。⑶处如果反对SLAB,则先尝试从SLAB中获取内存,否则执行⑷调用函数OsMemAllocWithCheck(pool, size)申请内存块。
LITE_OS_SEC_TEXT VOID *LOS_MemAlloc(VOID *pool, UINT32 size){ VOID *ptr = NULL; UINT32 intSave;⑴ if ((pool == NULL) || (size == 0)) { return NULL; } if (g_MALLOC_HOOK != NULL) { g_MALLOC_HOOK(); } MEM_LOCK(intSave); do {⑵ if (OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size)) { break; }⑶ ptr = OsSlabMemAlloc(pool, size); if (ptr == NULL) {⑷ ptr = OsMemAllocWithCheck(pool, size); } } while (0);#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordMalloc(ptr, size);#endif OsLmsSetAfterMalloc(ptr); MEM_UNLOCK(intSave); LOS_TRACE(MEM_ALLOC, pool, (UINTPTR)ptr, size); return ptr;}
咱们持续剖析函数OsMemAllocWithCheck(pool, size)。⑴处获取内存池中第一个内存节点,⑵计算出对齐后的内存大小,而后调用函数OsMemFindSuitableFreeBlock()获取适宜的内存块,如果找不到适宜的内存块,函数返回NULL。⑶处如果找到的内存块大于须要的内存大小,则执行宰割操作。⑷处把已调配的内存节点从链表中删除,而后设置魔术字和应用该内存块的工作Id,而后标记该内存块已应用。⑸处如果开启宏LOSCFG_MEM_TASK_STAT,还须要做些记录操作,自行剖析即可。⑹处返回内存块的数据区的地址,这个是通过内存管制节点+1定位到数据区内存地址实现的。申请内存实现,调用申请内存的函数中能够应用申请的内存了。
STATIC VOID *OsMemAllocWithCheck(LosMemPoolInfo *pool, UINT32 size){ LosMemDynNode *allocNode = NULL; UINT32 allocSize;#ifdef LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK LosMemDynNode *tmpNode = NULL; LosMemDynNode *preNode = NULL;#endif⑴ const VOID *firstNode = (const VOID *)((UINT8 *)OS_MEM_HEAD_ADDR(pool) + OS_DLNK_HEAD_SIZE);#ifdef LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK if (OsMemIntegrityCheck(pool, &tmpNode, &preNode)) { OsMemIntegrityCheckError(tmpNode, preNode); return NULL; }#endif⑵ allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE); allocNode = OsMemFindSuitableFreeBlock(pool, allocSize); if (allocNode == NULL) { OsMemInfoAlert(pool, allocSize); return NULL; }⑶ if ((allocSize + OS_MEM_NODE_HEAD_SIZE + OS_MEM_ALIGN_SIZE) <= allocNode->selfNode.sizeAndFlag) { OsMemSplitNode(pool, allocNode, allocSize); }⑷ OsMemListDelete(&allocNode->selfNode.freeNodeInfo, firstNode); OsMemSetMagicNumAndTaskID(allocNode); OS_MEM_NODE_SET_USED_FLAG(allocNode->selfNode.sizeAndFlag);⑸ OS_MEM_ADD_USED(&pool->stat, OS_MEM_NODE_GET_SIZE(allocNode->selfNode.sizeAndFlag), OS_MEM_TASKID_GET(allocNode)); OsMemNodeDebugOperate(pool, allocNode, size);⑹ return (allocNode + 1);}
2.4 按指定字节对齐申请动态内存
咱们还能够应用函数VOID LOS_MemAllocAlign(VOID pool, UINT32 size, UINT32 boundary),从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存。该函数须要3个参数,VOID pool为内存池起始地址,UINT32 size为须要申请的内存大小,UINT32 boundary内存对齐数值。当申请内存后失去的内存地址VOID ptr,对齐后的内存地址为VOID *alignedPtr,二者的偏移值应用UINT32 gapSize保留。因为曾经按OS_MEM_ALIGN_SIZE内存对齐了,最大偏移值为boundary - OS_MEM_ALIGN_SIZE。上面剖析下源码。
⑴处对参数进行校验,内存池地址不能为空,申请的内存大小不能为0,对齐字节boundary不能为0,还须要是2的幂。⑵处校验下对齐内存后是否会数据溢出。⑶处计算对齐后须要申请的内存大小,而后判断内存大小数值没有已应用或已对齐标记。⑷处调用函数申请到内存VOID ptr,而后计算出对齐的内存地址VOID alignedPtr,如果二者相等则返回。⑸处计算出对齐内存的偏移值,⑹处获取申请到的内存的管制节点,设置已对齐标记。⑺对偏移值设置对齐标记,而后把偏移值保留在内存VOID *alignedPtr的前4个字节里。⑻处从新定向要返回的指针,实现申请对齐的内存。
LITE_OS_SEC_TEXT VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary){ UINT32 useSize; UINT32 gapSize; VOID *ptr = NULL; VOID *alignedPtr = NULL; LosMemDynNode *allocNode = NULL; UINT32 intSave;⑴ if ((pool == NULL) || (size == 0) || (boundary == 0) || !IS_POW_TWO(boundary) || !IS_ALIGNED(boundary, sizeof(VOID *))) { return NULL; } MEM_LOCK(intSave); do {⑵ if ((boundary - sizeof(gapSize)) > ((UINT32)(-1) - size)) { break; }⑶ useSize = (size + boundary) - sizeof(gapSize); if (OS_MEM_NODE_GET_USED_FLAG(useSize) || OS_MEM_NODE_GET_ALIGNED_FLAG(useSize)) { break; }⑷ ptr = OsMemAllocWithCheck(pool, useSize); alignedPtr = (VOID *)OS_MEM_ALIGN(ptr, boundary); if (ptr == alignedPtr) { break; }⑸ gapSize = (UINT32)((UINTPTR)alignedPtr - (UINTPTR)ptr);⑹ allocNode = (LosMemDynNode *)ptr - 1; OS_MEM_NODE_SET_ALIGNED_FLAG(allocNode->selfNode.sizeAndFlag);#ifdef LOSCFG_MEM_RECORDINFO allocNode->selfNode.originSize = size;#endif#ifdef LOSCFG_MEM_HEAD_BACKUP OsMemNodeSaveWithGapSize(allocNode, gapSize);#endif⑺ OS_MEM_NODE_SET_ALIGNED_FLAG(gapSize); *(UINT32 *)((UINTPTR)alignedPtr - sizeof(gapSize)) = gapSize;⑻ ptr = alignedPtr; } while (0);#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordMalloc(ptr, size);#endif OsLmsSetAfterMalloc(ptr); MEM_UNLOCK(intSave); LOS_TRACE(MEM_ALLOC_ALIGN, pool, (UINTPTR)ptr, size, boundary); return ptr;}
2.5 开释动态内存
对申请的内存块应用结束,咱们能够应用函数UINT32 LOS_MemFree(VOID pool, VOID ptr)来开释动静态内存,须要2个参数,VOID pool是初始化过的动态内存池地址。VOID ptr是须要开释的动态内存块的数据区的起始地址,留神这个不是内存管制节点的地址。上面剖析下源码。
⑴处对传入的参数先进行校验。⑵如果内存是从SLAB中申请的内存,须要开释到SLAB内存区。⑶处调用函数OsMemFree(pool, ptr)实现内存的开释。
LITE_OS_SEC_TEXT UINT32 LOS_MemFree(VOID *pool, VOID *ptr){ UINT32 ret; UINT32 intSave;⑴ if ((pool == NULL) || (ptr == NULL) || !IS_ALIGNED(pool, sizeof(VOID *)) || !IS_ALIGNED(ptr, sizeof(VOID *))) { return LOS_NOK; } MEM_LOCK(intSave);⑵ if (OsSlabMemFree(pool, ptr)) { ret = LOS_OK; goto OUT; }⑶ ret = OsMemFree(pool, ptr);OUT: OsLmsSetAfterFree(ptr); MEM_UNLOCK(intSave); LOS_TRACE(MEM_FREE, pool, (UINTPTR)ptr); return ret;}
咱们持续剖析下函数OsMemFree(pool, ptr)。⑴处获取gapSize,对于函数LOS_MemAlloc()申请的内存,gapSize对应管制节点LosMemCtlNode的成员变量sizeAndFlag;对于函数LOS_MemAllocAlign()申请的内存,gapSize对应内存对齐偏移值。对于第一种状况,只标记已应用,第二种状况只标记已对齐。⑵处示意如果既标记已应用,又标记已对齐,则返回谬误。⑶处获取内存管制节点,对于第二种状况这样的获取是谬误的。⑷处代码校准第二种状况,如果gapSize标记为已对齐,去除gapSize中的对齐标记,获取偏移值。⑸处对偏移值进行校验,而后执行⑹获取获取内存管制节点。⑺处进一步调用函数实现内存的开释。
UINT32 OsMemFree(VOID *pool, VOID *ptr){ UINT32 ret = LOS_NOK; UINT32 gapSize; LosMemDynNode *node = NULL; do {⑴ gapSize = *(UINT32 *)((UINTPTR)ptr - sizeof(UINT32));⑵ if (OS_MEM_NODE_GET_ALIGNED_FLAG(gapSize) && OS_MEM_NODE_GET_USED_FLAG(gapSize)) { PRINT_ERR("[%s:%d]gapSize:0x%x error\n", __FUNCTION__, __LINE__, gapSize); goto OUT; }⑶ node = (LosMemDynNode *)((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE);⑷ if (OS_MEM_NODE_GET_ALIGNED_FLAG(gapSize)) { gapSize = OS_MEM_NODE_GET_ALIGNED_GAPSIZE(gapSize);⑸ if ((gapSize & (OS_MEM_ALIGN_SIZE - 1)) || (gapSize > ((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE))) { PRINT_ERR("illegal gapSize: 0x%x\n", gapSize); break; }⑹ node = (LosMemDynNode *)((UINTPTR)ptr - gapSize - OS_MEM_NODE_HEAD_SIZE); }#ifndef LOSCFG_MEM_HEAD_BACKUP⑺ ret = OsDoMemFree(pool, ptr, node);#endif } while (0);#ifdef LOSCFG_MEM_HEAD_BACKUP ret = OsMemBackupCheckAndRetore(pool, ptr, node); if (!ret) { ret = OsDoMemFree(pool, ptr, node); }#endifOUT:#ifdef LOSCFG_MEM_RECORDINFO if (ret == LOS_NOK) { OsMemRecordFree(ptr, 0); }#endif return ret;}
咱们持续看下函数OsDoMemFree(),该函数进一步调用函数OsMemFreeNode(node, pool)实现内存开释。
LITE_OS_SEC_TEXT STATIC INLINE UINT32 OsDoMemFree(VOID *pool, const VOID *ptr, LosMemDynNode *node){ UINT32 ret = OsMemCheckUsedNode(pool, node); if (ret == LOS_OK) {#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordFree(ptr, node->selfNode.originSize);#endif OsMemFreeNode(node, pool); } return ret;}
函数OsMemFreeNode(node, pool)如下,持续剖析。⑴处获取动态内存池的第一个内存管制节点,⑵处去除已应用标记。⑶处解决前一个节点不为空,且没有应用的状况。⑷处执行内存节点合并,而后获取下一个节点nextNode,如果下一个节点也是未应用节点,则把下一个节点从链表中删除,并把闲暇节点进行合并。⑸处把前一个节点从链表中删除,基于合并后的内存节点大小从新挂载到链表上。
如果上一个节点已应用,无奈和上一个节点合并,则执行⑹获取下一个节点。如果下一个节点也是未应用节点,则把下一个节点从链表中删除,并把闲暇节点进行合并。⑺依据内存节点大小获取链表节点,而后把开释的内存节点挂载到链表上,实现内存节点的开释。
STATIC INLINE VOID OsMemFreeNode(LosMemDynNode *node, LosMemPoolInfo *pool){ LosMemDynNode *nextNode = NULL; LOS_DL_LIST *listNodeHead = NULL;⑴ const VOID *firstNode = (const VOID *)((UINT8 *)OS_MEM_HEAD_ADDR(pool) + OS_DLNK_HEAD_SIZE); OS_MEM_REDUCE_USED(&pool->stat, OS_MEM_NODE_GET_SIZE(node->selfNode.sizeAndFlag), OS_MEM_TASKID_GET(node));⑵ node->selfNode.sizeAndFlag = OS_MEM_NODE_GET_SIZE(node->selfNode.sizeAndFlag);#ifdef LOSCFG_MEM_HEAD_BACKUP OsMemNodeSave(node);#endif#ifdef LOSCFG_MEM_LEAKCHECK OsMemLinkRegisterRecord(node);#endif⑶ if ((node->selfNode.preNode != NULL) && !OS_MEM_NODE_GET_USED_FLAG(node->selfNode.preNode->selfNode.sizeAndFlag)) { LosMemDynNode *preNode = node->selfNode.preNode;⑷ OsMemMergeNode(node); nextNode = OS_MEM_NEXT_NODE(preNode); if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->selfNode.sizeAndFlag)) { OsMemListDelete(&nextNode->selfNode.freeNodeInfo, firstNode); OsMemMergeNode(nextNode); }⑸ OsMemListDelete(&(preNode->selfNode.freeNodeInfo), firstNode); listNodeHead = OS_MEM_HEAD(pool, preNode->selfNode.sizeAndFlag); OS_CHECK_NULL_RETURN(listNodeHead); OsMemListAdd(listNodeHead, &preNode->selfNode.freeNodeInfo, firstNode); } else {⑹ nextNode = OS_MEM_NEXT_NODE(node); if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->selfNode.sizeAndFlag)) { OsMemListDelete(&nextNode->selfNode.freeNodeInfo, firstNode); OsMemMergeNode(nextNode); }⑺ listNodeHead = OS_MEM_HEAD(pool, node->selfNode.sizeAndFlag); OS_CHECK_NULL_RETURN(listNodeHead); OsMemListAdd(listNodeHead, &node->selfNode.freeNodeInfo, firstNode); }}
2.6 从新申请动态内存
咱们还能够应用函数VOID LOS_MemRealloc(VOID pool, VOID ptr, UINT32 size),按size大小从新分配内存块,并将原内存块内容拷贝到新内存块。如果新内存块申请胜利,则开释原内存块。该函数须要3个参数,VOID pool为内存池起始地址,VOID *ptr为之前申请的内存地址,UINT32 size为从新申请的内存大小。上面剖析下源码。
⑴处对参数进行校验,内存池地址不能为空,内存大小不能含有已应用、已对齐标记。⑵处如果传入的内存地址为空,则等价于LOS_MemAlloc()函数。⑶如果传入size为0,等价于函数LOS_MemFree()。⑷如果开启反对SLAB,须要调用OsMemReallocSlab()函数从新申请SLAB内存。⑸处调用函数OsMemRealloc()进行从新申请内存。
LITE_OS_SEC_TEXT_MINOR VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size){ UINT32 intSave; VOID *newPtr = NULL; BOOL isSlabMem = FALSE;⑴ if (OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size) || (pool == NULL)) { return NULL; }⑵ if (ptr == NULL) { newPtr = LOS_MemAlloc(pool, size); goto OUT; }⑶ if (size == 0) { (VOID)LOS_MemFree(pool, ptr); goto OUT; } MEM_LOCK(intSave);⑷ newPtr = OsMemReallocSlab(pool, ptr, &isSlabMem, size); if (isSlabMem == TRUE) { goto OUT_UNLOCK; }⑸ newPtr = OsMemRealloc(pool, ptr, size);OUT_UNLOCK: MEM_UNLOCK(intSave);OUT: LOS_TRACE(MEM_REALLOC, pool, (UINTPTR)ptr, size); return newPtr;}
进一步看下函数OsMemRealloc()。⑴处获取内存对齐后的大小allocSize,⑵处获取内存对齐之前的地址,稍后会剖析该函数OsGetRealPtr()。⑶获取内存管制节点node,而后获取节点的大小nodeSize。⑷解决从新申请的内存小于等于现有的内存的状况,须要调用函数OsMemReAllocSmaller()进行宰割,宰割结束返回(VOID *)ptr即可。如果从新申请更大的内存,则执行⑸获取下一个节点,而后⑹解决下一个节点可用且两个节点大小之和大于等于从新申请内存的大小allocSize。执行⑺处的函数,合并节点从新分配内存。
如果间断的节点的大小不满足从新申请内存的大小,则执行⑻处函数从新申请内存。而后执行⑼把之前内存的数据复制到新申请的内存区域,复制失败的话,则把新申请的内存开释掉,并返回退出函数。如果复制胜利,继续执行⑽开释掉之前的节点。
STATIC VOID *OsMemRealloc(VOID *pool, VOID *ptr, UINT32 size){ LosMemDynNode *node = NULL; LosMemDynNode *nextNode = NULL; VOID *tmpPtr = NULL; VOID *realPtr = NULL; UINT32 nodeSize;⑴ UINT32 allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE);#ifdef LOSCFG_MEM_RECORDINFO const VOID *originPtr = ptr;#endif⑵ realPtr = OsGetRealPtr(pool, ptr); if (realPtr == NULL) { return NULL; }⑶ node = (LosMemDynNode *)((UINTPTR)realPtr - OS_MEM_NODE_HEAD_SIZE); if (OsMemCheckUsedNode(pool, node) != LOS_OK) {#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordFree(originPtr, 0);#endif return NULL; } nodeSize = OS_MEM_NODE_GET_SIZE(node->selfNode.sizeAndFlag);⑷ if (nodeSize >= allocSize) {#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordFree(originPtr, node->selfNode.originSize);#endif OsMemReAllocSmaller(pool, allocSize, node, nodeSize);#ifdef LOSCFG_MEM_RECORDINFO OsMemReallocNodeRecord(node, size, ptr);#endif return (VOID *)ptr; }⑸ nextNode = OS_MEM_NEXT_NODE(node);⑹ if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->selfNode.sizeAndFlag) && ((nextNode->selfNode.sizeAndFlag + nodeSize) >= allocSize)) {#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordFree(originPtr, node->selfNode.originSize);#endif⑺ OsMemMergeNodeForReAllocBigger(pool, allocSize, node, nodeSize, nextNode);#ifdef LOSCFG_MEM_RECORDINFO OsMemReallocNodeRecord(node, size, ptr);#endif return (VOID *)ptr; }⑻ tmpPtr = OsMemAllocWithCheck(pool, size); if (tmpPtr != NULL) {#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordMalloc(tmpPtr, size);#endif UINT32 gapSize = (UINT32)((UINTPTR)ptr - (UINTPTR)realPtr);⑼ if (memcpy_s(tmpPtr, size, ptr, (nodeSize - OS_MEM_NODE_HEAD_SIZE - gapSize)) != EOK) { (VOID)OsMemFree((VOID *)pool, (VOID *)tmpPtr); return NULL; }#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordFree(originPtr, node->selfNode.originSize);#endif⑽ OsMemFreeNode(node, pool); } return tmpPtr;}
咱们回过头来,持续看下函数OsGetRealPtr()。⑴获取内存对齐的偏移值,⑵如果偏移值同时标记为已应用和已对齐,则返回谬误。⑶如果偏移值标记为已对齐,则执行⑷去除对齐标记,获取单纯的偏移值。而后执行⑸,获取内存对齐之前的数据区内存地址。
STATIC VOID *OsGetRealPtr(const VOID *pool, VOID *ptr){ VOID *realPtr = ptr;⑴ UINT32 gapSize = *((UINT32 *)((UINTPTR)ptr - sizeof(UINT32)));⑵ if (OS_MEM_NODE_GET_ALIGNED_FLAG(gapSize) && OS_MEM_NODE_GET_USED_FLAG(gapSize)) {#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordFree(ptr, 0);#endif PRINT_ERR("[%s:%d]gapSize:0x%x error\n", __FUNCTION__, __LINE__, gapSize); return NULL; }⑶ if (OS_MEM_NODE_GET_ALIGNED_FLAG(gapSize)) {⑷ gapSize = OS_MEM_NODE_GET_ALIGNED_GAPSIZE(gapSize); if ((gapSize & (OS_MEM_ALIGN_SIZE - 1)) || (gapSize > ((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE - (UINTPTR)pool))) { PRINT_ERR("[%s:%d]gapSize:0x%x error\n", __FUNCTION__, __LINE__, gapSize);#ifdef LOSCFG_MEM_RECORDINFO OsMemRecordFree(ptr, 0);#endif return NULL; }⑸ realPtr = (VOID *)((UINTPTR)ptr - (UINTPTR)gapSize); } return realPtr;}
小结
本文率领大家一起分析了LiteOS动静内存模块bestfit算法的源代码,蕴含动态内存的构造体、动态内存池初始化、动态内存申请、开释等。感激浏览,如有任何问题、倡议,都能够留言给咱们: https://gitee.com/LiteOS/Lite... 。为了更容易找到LiteOS代码仓,倡议拜访 https://gitee.com/LiteOS/LiteOS ,关注Watch、点赞Star、并Fork到本人账户下,谢谢。
点击关注,第一工夫理解华为云陈腐技术~