LiteOS内核源码分析系列十四 动态内存Bestfit_little分配算法
LiteOS内核源码分析系列十四 动态内存Bestfit_little分配算法
内存管理模块管理系统的内存资源,它是操作系统的核心模块之一,主要包括内存的初始化、分配以及释放。
在系统运行过程中,内存管理模块通过对内存的申请/释放来管理用户和OS对内存的使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统的内存碎片问题。
Huawei LiteOS
的内存管理分为静态内存管理和动态内存管理,提供内存初始化、分配、释放等功能。
-
动态内存:在动态内存池中分配用户指定大小的内存块。
- 优点:按需分配。
- 缺点:内存池中可能出现碎片。
-
静态内存:在静态内存池中分配用户初始化时预设(固定)大小的内存块。
- 优点:分配和释放效率高,静态内存池中无碎片。
- 缺点:只能申请到初始化预设大小的内存块,不能按需申请。
上一系列分析了静态内存,我们开始分析动态内存。动态内存管理主要用于用户需要使用大小不等的内存块的场景。当用户需要使用内存时,可以通过操作系统的动态内存申请函数索取指定大小的内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。
LiteOS
动态内存支持bestfit
(也称为dlink
)和bestfit_little
两种内存管理算法。上一系系列已经分析过动态内存的bestfit
的算法,本文继续分析LiteOS
动态内存的bestfit_little
算法。
本文通过分析LiteOS
动态内存模块的源码,帮助读者掌握动态内存的使用。LiteOS
动态内存模块的源代码,均可以在LiteOS
开源站点https://gitee.com/LiteOS/LiteOS 获取。动态内存bestfit_little
算法的源代码、开发文档,示例程序代码如下:
-
LiteOS
内核动态内存源代码包括动态内存的
bestfit_little
算法私有头文件kernel\base\mem\bestfit_little\los_memory_internal.h、动态内存私有头文件kernel\base\include\los_memory_pri.h、内存头文件kernel\include\los_memory.h、C
源代码文件kernel\base\mem\bestfit_little\los_memory.c、C
源代码文件kernel\base\mem\bestfit_little\los_heap.c。 -
开发指南文档–内存
在线文档https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E5%86%85%E5%AD%98。
接下来,我们看下动态内存的结构体,动态内存初始化,动态内存常用操作的源代码。
1、动态内存结构体定义和常用宏定义
1.1 动态内存结构体定义
动态内存bestfit_little
算法的结构体有动态内存池信息结构体LosMemPoolInfo
即内存管理节点struct LosHeapManager
,动态内存节点结构体LosHeapNode
。本系列先不考虑对SLAB
内存的支持。
1.1.1 动态内存池信息结构体LosMemPoolInfo
在文件kernel\base\include\los_memory_pri.h
中,定义了内存池信息结构体LosMemPoolInfo
。动态内存bestfit
算法和bestfit_little
算法中都定义了该结构体,结构体名称一样,成员有差异,我们本系列看看bestfit_little
算法的结构体,源代码如下。三个主要的成员是内存池头节点struct LosHeapNode *head
、内存池尾节点struct LosHeapNode *tail
和内存池大小UINT32 size
。其他结构体需要开启相应的宏才生效,暂不讨论这些宏相关的特性。
typedef struct LosHeapManager {
struct LosHeapNode *head;
struct LosHeapNode *tail;
UINT32 size;
#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 动态内存池节点结构体LosHeapNode
在文件kernel\base\mem\bestfit_little\los_memory_internal.h
中,定义了动态内存池节点结构体LosHeapNode
。结构体源代码如下,非常简单,成员包含指向上一个节点的指针struct LosHeapNode *prev
,节点的大小size
,是否使用标记used
,是否对齐标记align
,数据区地址UINT8 data[0]
。
struct LosHeapNode {
struct LosHeapNode *prev;
#ifdef LOSCFG_MEM_TASK_STAT
UINT32 taskId;
#endif
UINT32 size : 30;
UINT32 used : 1;
UINT32 align : 1;
UINT8 data[0];
};
1.2 动态内存常用宏定义
我们先看看使用到的宏定义,这些宏非常重要,在分析源代码前需要熟悉下这些宏的定义。在文件kernel\base\mem\bestfit_little\los_heap.c
中定义了些和heap
操作相关的宏。HEAP_CAST(t, exp)
用于指针类型转换,HEAP_ALIGN
表示内存对齐大小,MALLOC_MAXSIZE
表示允许申请的最大内存。
#define HEAP_CAST(t, exp) ((t)(exp))
#define HEAP_ALIGN 4
#define MALLOC_MAXSIZE (0xFFFFFFFF - HEAP_ALIGN + 1)
动态内存头文件kernel\base\mem\bestfit\los_memory_internal.h
中也提供了一些重要的宏定义。
⑴处定义两个宏用于内存对齐,⑵处定义是否对齐标记位,即高31位。然后分别定义3个宏函数,OS_MEM_SET_ALIGN_FLAG()
设置标记为对齐,OS_MEM_GET_ALIGN_FLAG()
获取是否已对齐,OS_MEM_GET_ALIGN_GAPSIZE()
获取去除标记后的使用大小。
⑴ #define ALIGNE(sz) (((sz) + HEAP_ALIGN - 1) & (~(HEAP_ALIGN - 1)))
#define OS_MEM_ALIGN(value, align) (((UINT32)(UINTPTR)(value) + (UINT32)((align) - 1)) & \
(~(UINT32)((align) - 1)))
⑵ #define OS_MEM_ALIGN_FLAG 0x80000000
#define OS_MEM_SET_ALIGN_FLAG(align) ((align) = ((align) | OS_MEM_ALIGN_FLAG))
#define OS_MEM_GET_ALIGN_FLAG(align) ((align) & OS_MEM_ALIGN_FLAG)
#define OS_MEM_GET_ALIGN_GAPSIZE(align) ((align) & (~OS_MEM_ALIGN_FLAG))
2、动态内存常用操作
Huawei LiteOS
系统中的动态内存管理模块为用户提供初始化和删除内存池、申请、释放动态内存等操作,我们来分析下接口的源代码。在分析下内存操作接口之前,我们先看下一下常用的内部heap
接口。
2.1 动态内存内部Heap接口
在文件kernel\base\mem\bestfit_little\los_heap.c
中定义了内存操作相关的接口,如初始化内存池、申请、释放动态内存等。
2.1.1 堆内存初始化OsHeapInit
函数BOOL OsHeapInit(VOID *pool, UINT32 size)
用于初始化堆内存,需要2个参数:VOID *pool
是内存池的起始地址,UINT32 size
为内存池的大小。⑴处转换内存池起始地址为内存管理节点,⑵进行参数校验,如果内存池地址为空,内存池大小小于内存管理节点和一个内存节点的大小之和,则返回初始化失败。⑶清除内存池的内容,⑷设置内存管理节点的size
成员变量数据为内存节点区的大小。⑸处获取第一个内存节点的地址,该节点为内存管理节点的头结点,也是尾节点。⑹设置内存节点相关信息,大小、前一个节点指针,内存节点的数据区大小。⑺处内存使用统计暂不分析,自行查看即可。
BOOL OsHeapInit(VOID *pool, UINT32 size)
{
struct LosHeapNode *node = NULL;
⑴ struct LosHeapManager *heapMan = HEAP_CAST(struct LosHeapManager *, pool);
⑵ if ((heapMan == NULL) || (size <= (sizeof(struct LosHeapNode) + sizeof(struct LosHeapManager)))) {
return FALSE;
}
⑶ (VOID)memset_s(pool, size, 0, size);
⑷ heapMan->size = size - sizeof(struct LosHeapManager);
⑸ node = heapMan->head = (struct LosHeapNode *)((UINT8*)pool + sizeof(struct LosHeapManager));
heapMan->tail = node;
⑹ node->used = 0;
node->prev = NULL;
node->size = heapMan->size - sizeof(struct LosHeapNode);
⑺ OsHeapStatInit(heapMan, size);
return TRUE;
}
2.1.2 申请内存OsHeapAlloc
函数VOID *OsHeapAlloc(VOID *pool, UINT32 size)
用于申请内存块,VOID *pool
为内存池起始地址,UINT32 size
为要申请的内存大小。⑴处计算内存对齐后的大小,⑵转换为内存管理节点,然后进行参数校验,确保内存池地址不为空,申请的内存大小不超标。⑶处获取内存尾节点,当不为空时,循环遍历各个内存节点,寻找出最佳的满足申请大小的内存节点best
。⑷处的条件表示,当内存节点未使用,节点大小满足申请的内存大小,并且内存节点的大小小于已遍历的最佳节点时,把当前节点设置为最佳的节点。⑸如果节点大小刚刚好,跳转到SIZE_MATCH
标签。
⑹如果最佳节点为空,表示申请失败,返回。⑺处处理最佳节点的大小超出申请的大小的情况,⑻处表示去掉申请的内存块后剩余的内存节点node
,然后标记该剩余节点的信息,包含未使用标记,大小,上一个节点等信息。⑼如果最佳内存节点不是尾节点,执行⑽后续下一个节点,把下一个内存节点的上一个节点设置为剩余内存节点node
。⑾表示如果最佳节点是尾节点,则把剩余节点设置为尾节点。⑿处的SIZE_MATCH
把申请的内存节点设置为已使用,然后把返回指针设置为节点的数据区起始地址best->data
。
VOID *OsHeapAlloc(VOID *pool, UINT32 size)
{
struct LosHeapNode *node = NULL;
struct LosHeapNode *next = NULL;
struct LosHeapNode *best = NULL;
VOID *ptr = NULL;
⑴ UINT32 alignSize = ALIGNE(size);
⑵ struct LosHeapManager *heapMan = HEAP_CAST(struct LosHeapManager *, pool);
if ((heapMan == NULL) || (size > MALLOC_MAXSIZE)) {
return NULL;
}
if (OsHeapIntegrityCheck(heapMan) != LOS_OK) {
return NULL;
}
⑶ node = heapMan->tail;
while (node != NULL) {
⑷ if ((node->used == 0) && (node->size >= alignSize) &&
((best == NULL) || (best->size > node->size))) {
best = node;
⑸ if (best->size == alignSize) {
goto SIZE_MATCH;
}
}
node = node->prev;
}
⑹ if (best == NULL) {
PRINT_ERR("there's not enough whole to alloc 0x%x Bytes!\n", alignSize);
goto OUT;
}
⑺ if ((best->size - alignSize) > sizeof(struct LosHeapNode)) {
⑻ node = (struct LosHeapNode*)(UINTPTR)(best->data + alignSize);
node->used = 0;
node->size = best->size - alignSize - sizeof(struct LosHeapNode);
node->prev = best;
⑼ if (best != heapMan->tail) {
⑽ next = OsHeapPrvGetNext(heapMan, node);
if (next != NULL) {
next->prev = node;
}
} else {
⑾ heapMan->tail = node;
}
best->size = alignSize;
}
SIZE_MATCH:
⑿ best->align = 0;
best->used = 1;
ptr = best->data;
OsHeapStatAddUsed(heapMan, best);
OUT:
return ptr;
}
2.1.3 按指定字节内存对齐申请内存OsHeapAllocAlign
函数VOID* OsHeapAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)
用于从指定动态内存池中申请长度为size
且地址按boundary
字节对齐的内存。该函数需要3个参数,VOID *pool
为内存池起始地址,UINT32 size
为需要申请的内存大小,UINT32 boundary
内存对齐数值。当使用函数OsHeapAlloc()
申请内存后得到的内存地址VOID *ptr
,对齐后的内存地址为VOID *alignedPtr
,二者的偏移值使用UINT32 gapSize
保存。下面分析下源码。
⑴处对参数进行校验,内存池地址不能为空,申请的内存大小不能为0,对齐字节boundary
不能小于4,且按自身进行对齐。⑵处计算对齐后需要申请的内存大小,⑶处调用函数申请到内存VOID *ptr
,然后计算出对齐的内存地址VOID *alignedPtr
,如果二者相等则返回。⑷处计算出对齐内存的偏移值,对偏移值设置对齐标记,然后执行⑸把偏移值保存在内存VOID *alignedPtr
的前4个字节里。重新定向要返回的指针,完成申请对齐的内存。
VOID* OsHeapAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)
{
UINT32 useSize;
UINT32 gapSize;
VOID *ptr = NULL;
VOID *alignedPtr = NULL;
⑴ if ((pool == NULL) || (size == 0) || (boundary < sizeof(VOID *)) || !IS_ALIGNED(boundary, boundary)) {
return NULL;
}
⑵ useSize = (size + boundary) - sizeof(VOID*);
if (useSize < size) {
return NULL;
}
⑶ ptr = OsHeapAlloc(pool, useSize);
if (ptr != NULL) {
alignedPtr = (VOID *)(UINTPTR)OS_MEM_ALIGN(ptr, boundary);
if (alignedPtr == ptr) {
goto OUT;
}
⑷ gapSize = (UINTPTR)alignedPtr - (UINTPTR)ptr;
OS_MEM_SET_ALIGN_FLAG(gapSize);
⑸ *((UINT32 *)((UINTPTR)alignedPtr - sizeof(UINTPTR))) = gapSize;
ptr = alignedPtr;
}
OUT:
return ptr;
}
2.1.4 释放内存OsHeapFree
BOOL OsHeapFree(VOID *pool, VOID *ptr)
函数用于从内存池释放内存,需要2个参数,VOID *pool
是初始化过的静态内存池地址。VOID *ptr
是需要释放的动态内存块的数据区的起始地址,注意这个不是内存控制节点的地址。下面分析下源码。
⑴处转换内存池起始地址获取管理节点,然后对传入的参数先进行校验。⑵处获取偏移值gapSize
。⑶处如果偏移值设置了对齐标记,表示是要释放使用OsHeapAllocAlign()
函数申请的内存。⑷去除对齐标记,获取实际的偏移值,然后计算出内存的实际未对齐的指针ptr
。如果是使用OsHeapAlloc()
申请的内存,不存在偏移值,gapSize
其实就是内存管理节点结构体的成员变量size
的值。
继续执行⑸进行校验,如果指针*ptr
比头节点还小,或比尾节点还大,则报错返回。⑹基于数据地址获取节点地址,然后下一步对该节点进行验证,如果验证失败则返回错误。调用⑺处函数OsHeapDoFree()
进行内存释放。
BOOL OsHeapFree(VOID *pool, VOID *ptr)
{
struct LosHeapNode *node = NULL;
UINT32 gapSize;
BOOL ret = TRUE;
⑴ struct LosHeapManager *heapMan = HEAP_CAST(struct LosHeapManager *, pool);
if ((heapMan == NULL) || (ptr == NULL)) {
return LOS_NOK;
}
⑵ gapSize = *((UINT32 *)((UINTPTR)ptr - sizeof(UINTPTR)));
⑶ if (OS_MEM_GET_ALIGN_FLAG(gapSize)) {
⑷ gapSize = OS_MEM_GET_ALIGN_GAPSIZE(gapSize);
ptr = (VOID *)((UINTPTR)ptr - gapSize);
}
⑸ if (((UINTPTR)ptr < (UINTPTR)heapMan->head) ||
((UINTPTR)ptr > ((UINTPTR)heapMan->tail + sizeof(struct LosHeapNode)))) {
PRINT_ERR("0x%lx out of range!\n", (UINTPTR)ptr);
return FALSE;
}
⑹ node = ((struct LosHeapNode *)ptr) - 1;
if ((node->used == 0) || (!((UINTPTR)node == (UINTPTR)heapMan->head) &&
(((UINTPTR)node->prev < (UINTPTR)heapMan->head) ||
((UINTPTR)node->prev > ((UINTPTR)heapMan->tail + sizeof(struct LosHeapNode))) ||
((UINTPTR)OsHeapPrvGetNext(heapMan, node->prev) != (UINTPTR)node)))) {
ret = FALSE;
goto OUT;
}
OsHeapStatDecUsed(heapMan, node);
⑺ OsHeapDoFree(heapMan, node);
OUT:
return ret;
}
我们看下函数OsHeapDoFree()
。⑴把要释放的节点设置为未使用状态,⑵处循环遍历要释放的节点前序节点,指向第一个未使用的节点,然后执行⑶获取第一个未使用的节点的下一个节点。⑷从第一未使用的节点循环遍历后续每一个节点。⑸如果遍历到使用中的节点,把使用的节点的前序节点设置为node
,然后跳出循环。⑹如果遍历的节点都是未使用的节点,则把大小合并到第一个节点。⑺如果遍历到尾节点,则更新尾节点为node
。⑻更新循环条件,持续获取下一个节点。
STATIC VOID OsHeapDoFree(struct LosHeapManager *heapMan, struct LosHeapNode *curNode)
{
struct LosHeapNode *node = curNode;
struct LosHeapNode *next = NULL;
⑴ node->used = 0;
⑵ while ((node->prev) && (!node->prev->used)) {
node = node->prev;
}
⑶ next = OsHeapPrvGetNext(heapMan, node);
⑷ while (next != NULL) {
⑸ if (next->used) {
next->prev = node;
break;
}
⑹ node->size += (sizeof(struct LosHeapNode) + next->size);
⑺ if (heapMan->tail == next) {
heapMan->tail = node;
}
⑻ next = OsHeapPrvGetNext(heapMan, node);
}
}
2.2 动态内存对外操作接口
2.2.1 内存初始化LOS_MemInit
我们分析下初始化动态内存池函数UINT32 LOS_MemInit(VOID *pool, UINT32 size)
的代码。我们先看看函数参数,VOID *pool
是动态内存池的起始地址,UINT32 size
是初始化的动态内存池的总大小,size
需要小于等于*pool
开始的内存区域的大小,否则会影响后面的内存区域,还需要大于动态内存管理节点大小sizeof(struct LosHeapManager)
。
我们看下代码,⑴处对传入参数进行校验。⑵处调用函数OsHeapInit()
进行内存池初始化,这是初始化的内存的核心函数。⑶处开启宏LOSCFG_KERNEL_MEM_SLAB_EXTENTION
支持时,才会执行。
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MemInit(VOID *pool, UINT32 size)
{
UINT32 ret = LOS_NOK;
UINT32 intSave;
⑴ if ((pool == NULL) || (size <= sizeof(struct LosHeapManager))) {
return ret;
}
MEM_LOCK(intSave);
if (OsMemMulPoolInit(pool, size) != LOS_OK) {
goto OUT;
}
⑵ if (OsHeapInit(pool, size) == FALSE) {
(VOID)OsMemMulPoolDeinit(pool);
goto OUT;
}
⑶ OsSlabMemInit(pool, size);
ret = LOS_OK;
OUT:
MEM_UNLOCK(intSave);
LOS_TRACE(MEM_INFO_REQ, pool);
return ret;
}
2.2.2 申请动态内存LOS_MemAlloc
初始化动态内存池后,我们可以使用函数VOID *LOS_MemAlloc(VOID *pool, UINT32 size)
来申请动态内存,下面分析下源码。
⑴处对参数进行校验,内存池地址不能为空,申请的内存大小不能为0。⑵处如果支持SLAB
,则先尝试从SLAB
中获取内存,否则执行⑶调用函数OsHeapAlloc(pool, size)
申请内存块。
LITE_OS_SEC_TEXT VOID *LOS_MemAlloc(VOID *pool, UINT32 size)
{
VOID *ptr = NULL;
UINT32 intSave;
⑴ if ((pool == NULL) || (size == 0)) {
return ptr;
}
MEM_LOCK(intSave);
⑵ ptr = OsSlabMemAlloc(pool, size);
if (ptr == NULL) {
⑶ ptr = OsHeapAlloc(pool, size);
}
MEM_UNLOCK(intSave);
LOS_TRACE(MEM_ALLOC, pool, (UINTPTR)ptr, size);
return ptr;
}
2.2.3 按指定字节对齐申请动态内存LOS_MemAllocAlign
我们还可以使用函数VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)
,从指定动态内存池中申请长度为size
且地址按boundary
字节对齐的内存。该函数需要3个参数,VOID *pool
为内存池起始地址,UINT32 size
为需要申请的内存大小,UINT32 boundary
内存对齐数值。当申请内存后得到的内存地址VOID *ptr
,对齐后的内存地址为VOID *alignedPtr
,二者的偏移值使用UINT32 gapSize
保存。代码如下,可以看出实际申请内存使用的是函数OsHeapAllocAlign()
。
LITE_OS_SEC_TEXT VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)
{
VOID *ptr = NULL;
UINT32 intSave;
MEM_LOCK(intSave);
ptr = OsHeapAllocAlign(pool, size, boundary);
MEM_UNLOCK(intSave);
LOS_TRACE(MEM_ALLOC_ALIGN, pool, (UINTPTR)ptr, size, boundary);
return ptr;
}
2.2.4 释放动态内存
对申请的内存块使用完毕,我们可以使用函数UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
来释放静态内存,需要2个参数,VOID *pool
是初始化过的静态内存池地址。VOID *ptr
是需要释放的动态内存块的数据区的起始地址,注意这个不是内存控制节点的地址。下面分析下源码。
⑴处对传入的参数先进行校验。⑵如果内存是从SLAB
中申请的内存,需要释放到SLAB
内存区。⑶处调用函数OsHeapFree(pool, mem)
完成内存的释放。
LITE_OS_SEC_TEXT UINT32 LOS_MemFree(VOID *pool, VOID *mem)
{
BOOL ret = FALSE;
UINT32 intSave;
⑴ if ((pool == NULL) || (mem == NULL)) {
return LOS_NOK;
}
MEM_LOCK(intSave);
⑵ ret = OsSlabMemFree(pool, mem);
if (ret != TRUE) {
⑶ ret = OsHeapFree(pool, mem);
}
MEM_UNLOCK(intSave);
LOS_TRACE(MEM_FREE, pool, (UINTPTR)mem);
return (ret == TRUE ? LOS_OK : LOS_NOK);
}
2.2.5 重新申请动态内存
我们还可以使用函数VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size)
,按size
大小重新分配内存块,并将原内存块内容拷贝到新内存块。如果新内存块申请成功,则释放原内存块。该函数需要3个参数,VOID *pool
为内存池起始地址,VOID *ptr
为之前申请的内存地址,UINT32 size
为重新申请的内存大小。下面分析下源码。
⑴处如果内存池地址不为空,入size
为0,等价于函数LOS_MemFree()
。⑵处如果传入的内存地址为空,则等价于LOS_MemAlloc()
函数。⑶处获取内存对齐偏移值,并校验是否包含对齐标记,如果内存对齐函数LOS_MemAllocAlign()
申请的内存,则返回错误,无法重现申请内存。⑷处获取内存节点地址,⑸计算出需要复制的内存空间的大小。⑹按指定的大小申请内存,然后执行⑺复制内存数据。⑻处如果复制数据成功则释放之前的内存,如果复制失败则释放重新申请的内存。执行⑼释放掉内存。
VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size)
{
VOID *retPtr = NULL;
VOID *freePtr = NULL;
UINT32 intSave;
struct LosHeapNode *node = NULL;
UINT32 cpySize;
UINT32 gapSize;
errno_t rc;
⑴ if ((ptr != NULL) && (size == 0)) {
if (LOS_MemFree(pool, ptr) != LOS_OK) {
PRINT_ERR("LOS_MemFree error, pool[%p], pPtr[%p]\n", pool, ptr);
}
⑵ } else if (ptr == NULL) {
retPtr = LOS_MemAlloc(pool, size);
} else {
MEM_LOCK(intSave);
UINT32 oldSize = OsSlabMemCheck(pool, ptr);
if (oldSize != (UINT32)(-1)) {
cpySize = (size > oldSize) ? oldSize : size;
} else {
⑶ gapSize = *((UINTPTR *)((UINTPTR)ptr - sizeof(UINTPTR)));
if (OS_MEM_GET_ALIGN_FLAG(gapSize)) {
MEM_UNLOCK(intSave);
return NULL;
}
⑷ node = ((struct LosHeapNode *)ptr) - 1;
⑸ cpySize = (size > (node->size)) ? (node->size) : size;
}
MEM_UNLOCK(intSave);
⑹ retPtr = LOS_MemAlloc(pool, size);
if (retPtr != NULL) {
⑺ rc = memcpy_s(retPtr, size, ptr, cpySize);
⑻ if (rc == EOK) {
freePtr = ptr;
} else {
freePtr = retPtr;
retPtr = NULL;
}
⑼ if (LOS_MemFree(pool, freePtr) != LOS_OK) {
PRINT_ERR("LOS_MemFree error, pool[%p], ptr[%p]\n", pool, freePtr);
}
}
}
LOS_TRACE(MEM_REALLOC, pool, (UINTPTR)ptr, size);
return retPtr;
}
小结
本文带领大家一起剖析了LiteOS
动态内存模块bestfit_little
算法的源代码,包含动态内存的结构体、动态内存池初始化、动态内存申请、释放等。感谢阅读,如有任何问题、建议,都可以留言给我们: https://gitee.com/LiteOS/LiteOS/issues 。为了更容易找到LiteOS
代码仓,建议访问 https://gitee.com/LiteOS/LiteOS ,关注Watch
、点赞Star
、并Fork
到自己账户下,谢谢。
- 点赞
- 收藏
- 关注作者
评论(0)