LiteOS内核源码分析系列十四 动态内存Bestfit_little分配算法

网友投稿 664 2022-05-30

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

LiteOS内核源码分析系列十四 动态内存Bestfit_little分配算法

我们还可以使用函数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到自己账户下,谢谢。

轻量级操作系统 LiteOS

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Docker on 华为云DevCloud
下一篇:jQuery Ajax 实例 全解析(转载)
相关文章