摘要:本文率领大家一起分析了鸿蒙轻内核的动静内存模块的源代码,蕴含动态内存的构造体、动态内存池初始化、动态内存申请、开释等。

本文分享自华为云社区《鸿蒙轻内核M核源码剖析系列九 动态内存Dynamic Memory 第一局部》,原文作者:zhushy。

内存治理模块管理系统的内存资源,它是操作系统的外围模块之一,次要包含内存的初始化、调配以及开释。

在零碎运行过程中,内存治理模块通过对内存的申请/开释来治理用户和OS对内存的应用,使内存的利用率和应用效率达到最优,同时最大限度地解决零碎的内存碎片问题。

鸿蒙轻内核的内存治理分为动态内存治理和动态内存治理,提供内存初始化、调配、开释等性能。

  • 动态内存:在动态内存池中调配用户指定大小的内存块。
    长处:按需分配。
    毛病:内存池中可能呈现碎片。
  • 动态内存:在动态内存池中调配用户初始化时预设(固定)大小的内存块。
    长处:调配和开释效率高,动态内存池中无碎片。
    毛病:只能申请到初始化预设大小的内存块,不能按需申请。

上一系列剖析了动态内存,咱们开始剖析动态内存。动态内存治理次要用于用户须要应用大小不等的内存块的场景。当用户须要应用内存时,能够通过操作系统的动态内存申请函数索取指定大小的内存块,一旦应用结束,通过动态内存开释函数偿还所占用内存,使之能够重复使用。

OpenHarmony LiteOS-M动态内存在TLSF算法的根底上,对区间的划分进行了优化,取得更优的性能,升高了碎片率。动态内存外围算法框图如下:

依据闲暇内存块的大小,应用多个闲暇链表来治理。依据内存闲暇块大小分为两个局部:[4, 127]和[27, 231],如上图size class所示:

  • 对[4,127]区间的内存进行等分,如上图绿色局部所示,分为31个小区间,每个小区间对应内存块大小为4字节的倍数。每个小区间对应一个闲暇内存链表和用于标记对应闲暇内存链表是否为空的一个比特位,值为1时,闲暇链表非空。[4,127]区间的内存应用1个32位无符号整数位图标记。
  • 大于127字节的闲暇内存块,依照2的次幂区间大小进行闲暇链表治理。总共分为24个小区间,每个小区间又等分为8个二级小区间,见上图蓝色的Size Class和Size SubClass局部。每个二级小区间对应一个闲暇链表和用于标记对应闲暇内存链表是否为空的一个比特位。总共24*8=192个二级小区间,对应192个闲暇链表和192/32=6个32位无符号整数位图标记。

例如,当有40字节的闲暇内存须要插入闲暇链表时,对应小区间[40,43],第10个闲暇链表,位图标记的第10比特位。把40字节的闲暇内存挂载第10个闲暇链表上,并判断是否须要更新位图标记。当须要申请40字节的内存时,依据位图标记获取存在满足申请大小的内存块的闲暇链表,从闲暇链表上获取闲暇内存节点。如果调配的节点大于须要申请的内存大小,进行宰割节点操作,残余的节点从新挂载到相应的闲暇链表上。当有580字节的闲暇内存须要插入闲暇链表时,对应二级小区间[2^9,2^9+2^6],第31+2*8=47个闲暇链表,第2个位图标记的第17比特位。把580字节的闲暇内存挂载第47个闲暇链表上,并判断是否须要更新位图标记。当须要申请580字节的内存时,依据位图标记获取存在满足申请大小的内存块的闲暇链表,从闲暇链表上获取闲暇内存节点。如果调配的节点大于须要申请的内存大小,进行宰割节点操作,残余的节点从新挂载到相应的闲暇链表上。如果对应的闲暇链表为空,则向更大的内存区间去查问是否有满足条件的闲暇链表,理论计算时,会一次性查找到满足申请大小的闲暇链表。

动态内存治理构造如下图所示:

  • 内存池池头局部

内存池池头局部蕴含内存池信息和位图标记数组和闲暇链表数组。内存池信息蕴含内存池起始地址及堆区域总大小,内存池属性。位图标记数组有7个32位无符号整数组成,每个比特位标记对应的闲暇链表是否挂载闲暇内存块节点。闲暇内存链表蕴含223个闲暇内存头节点信息,每个闲暇内存头节点信息保护内存节拍板和闲暇链表中的前驱、后继闲暇内存节点。

  • 内存池节点局部

蕴含3种类型节点,未应用闲暇内存节点,已应用内存节点,尾节点。每个内存节点保护一个前序指针,指向内存池中上一个内存节点,保护大小和应用标记,标记该内存节点的大小和是否应用等。闲暇内存节点和已应用内存节点前面的数据域,尾节点没有数据域。

本文通过剖析动静内存模块的源码,帮忙读者把握动态内存的应用。本文中所波及的源码,以OpenHarmony LiteOS-M内核为例,均能够在开源站点 https://gitee.com/openharmony... 获取 。接下来,咱们看下动态内存的构造体,动态内存初始化,动态内存罕用操作的源代码。

1、动态内存构造体定义和罕用宏定义

1.1 动态内存构造体定义

动态内存的构造体有动态内存池信息结构体OsMemPoolInfo,动态内存池头构造体OsMemPoolHead、动态内存节拍板构造体OsMemNodeHead,已应用内存节点构造体OsMemUsedNodeHead,闲暇内存节点构造体OsMemFreeNodeHead。这些构造体定义在文件kernel\src\mm\los_memory.c中,下文会联合上文的动态内存治理构造示意图对各个构造体的成员变量进行阐明。

1.1.1 动态内存池池头相干构造体

动态内存池信息结构体OsMemPoolInfo保护内存池的开始地址和大小信息。三个次要的成员是内存池开始地址.pool,内存池大小.poolSize和内存值属性.attr。如果开启宏LOSCFG_MEM_WATERLINE,还会保护内存池的水线数值。

struct OsMemPoolInfo {    VOID *pool;               /* 内存池的内存开始地址 */    UINT32 totalSize;         /* 内存池总大小 */    UINT32 attr;              /* 内存池属性 */#if (LOSCFG_MEM_WATERLINE == 1)    UINT32 waterLine;         /* 内存池中内存最大应用值 */    UINT32 curUsedSize;       /* 内存池中以后已应用的大小 */#endif};

动态内存池头构造体OsMemPoolHead源码如下,除了动态内存池信息结构体struct OsMemPoolInfo info,还保护2个数组,一个是闲暇内存链表位图数组freeListBitmap[],一个是闲暇内存链表数组freeList[]。宏定义OS_MEM_BITMAP_WORDS和OS_MEM_FREE_LIST_COUNT后文会介绍。

struct OsMemPoolHead {    struct OsMemPoolInfo info;    UINT32 freeListBitmap[OS_MEM_BITMAP_WORDS];    struct OsMemFreeNodeHead *freeList[OS_MEM_FREE_LIST_COUNT];#if (LOSCFG_MEM_MUL_POOL == 1)    VOID *nextPool;#endif};

1.1.2 动态内存池内存节点相干构造体

先看下动态内存节拍板构造体OsMemNodeHead的定义,⑴处如果开启内存节点完整性检查的宏LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK,会保护魔术字.magic进行校验。⑵处如果开启内存透露查看的宏,会保护链接寄存器数组linkReg[]。⑶处的成员变量是个指针组合体,内存池中的每个内存节拍板保护指针执行上一个内存节点。⑷处保护内存节点的大小和标记信息。

struct OsMemNodeHead {  #if (LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK == 1)⑴    UINT32 magic;  #endif  #if (LOSCFG_MEM_LEAKCHECK == 1)⑵    UINTPTR linkReg[LOSCFG_MEM_RECORD_LR_CNT];  #endif      union {          struct OsMemNodeHead *prev; /* The prev is used for current node points to the previous node */          struct OsMemNodeHead *next; /* The next is used for sentinel node points to the expand node */⑶     } ptr; #if (LOSCFG_MEM_FREE_BY_TASKID == 1)⑷    UINT32 taskID : 6;      UINT32 sizeAndFlag : 26;  #else      UINT32 sizeAndFlag;  #endif  };

接着看下已应用内存节点构造体OsMemUsedNodeHead,该构造体比较简单,间接以动态内存节拍板构造体OsMemNodeHead作为惟一的成员。

struct OsMemUsedNodeHead {    struct OsMemNodeHead header;};

咱们再看下闲暇内存节点构造体OsMemFreeNodeHead,除了动态内存节拍板构造体OsMemNodeHead成员,还蕴含2个指针别离指向上一个和下一个闲暇内存节点。

struct OsMemFreeNodeHead {    struct OsMemNodeHead header;    struct OsMemFreeNodeHead *prev;    struct OsMemFreeNodeHead *next;};

1.2 动态内存外围算法相干的宏和函数

动态内存中还提供了一些和TLSF算法相干的宏定义和内联函数,这些宏十分重要,在剖析源代码前须要相熟下这些宏的定义。能够联合上文的动态内存外围算法框图进行学习。⑴处的宏对处于[2^n,2^(n+1)],其中(n=7,8,…30)区间的大内存块进行2^3=8等分。⑵处的宏,定义处于[4,127]区间的小内存块划分为31个,即4,8,12,…,124。⑶处定义小内存的上界值,思考内存对齐和粒度,最大值只能取到124。

⑷处的宏示意处于[2^n,2^(n+1)],其中(n=7,8,…30)区间的大内存分为24个小区间,其中n=7 就是⑺处定义的宏OS_MEM_LARGE_START_BUCKET。⑻处对应闲暇内存链表的长度。⑼处是闲暇链表位图数组的长度,31个小内存应用1个位图字,所以须要加1。⑽处定义位图掩码,每个位图字是32位无符号整数。

持续看下内联函数。⑾处函数查找位图字中的第一个1的比特位,这个实现的性能相似内建函数__builtin_ctz。该函数用于获取闲暇内存链表对应的位图字中,第一个挂载着闲暇内存块的闲暇内存链表。⑿处获取位图字中的最初一个1的比特位,(从32位二进制数值从左到右顺次第0,1,…,31位)。⒀处函数名称中的Log是对数英文logarithm的缩写,函数用于计算以2为底的对数的整数局部。⒁处获取内存区间的大小级别编号,对于小于128字节的,有31个级别,对处于[2^n,2^(n+1)],其中(n=7,8,…30)区间的内存,有24个级别。⒂处依据内存大小,内存区间一级编号获取获取二级小区间的编号,对处于[2^n,2^(n+1)],其中(n=7,8,…30)区间的内存,有8个二级小区间。

    /* The following is the macro definition and interface implementation related to the TLSF. */    /* Supposing a Second Level Index: SLI = 3. */⑴  #define OS_MEM_SLI                      3    /* Giving 1 free list for each small bucket: 4, 8, 12, up to 124. */⑵  #define OS_MEM_SMALL_BUCKET_COUNT       31⑶  #define OS_MEM_SMALL_BUCKET_MAX_SIZE    128    /* Giving 2^OS_MEM_SLI free lists for each large bucket. */⑷  #define OS_MEM_LARGE_BUCKET_COUNT       24    /* OS_MEM_SMALL_BUCKET_MAX_SIZE to the power of 2 is 7. */⑺  #define OS_MEM_LARGE_START_BUCKET       7    /* The count of free list. */⑻  #define OS_MEM_FREE_LIST_COUNT  (OS_MEM_SMALL_BUCKET_COUNT + (OS_MEM_LARGE_BUCKET_COUNT << OS_MEM_SLI))    /* The bitmap is used to indicate whether the free list is empty, 1: not empty, 0: empty. */⑼  #define OS_MEM_BITMAP_WORDS     ((OS_MEM_FREE_LIST_COUNT >> 5) + 1)⑽  #define OS_MEM_BITMAP_MASK 0x1FU    /* Used to find the first bit of 1 in bitmap. */⑾  STATIC INLINE UINT16 OsMemFFS(UINT32 bitmap)    {        bitmap &= ~bitmap + 1;        return (OS_MEM_BITMAP_MASK - CLZ(bitmap));    }    /* Used to find the last bit of 1 in bitmap. */⑿  STATIC INLINE UINT16 OsMemFLS(UINT32 bitmap)    {        return (OS_MEM_BITMAP_MASK - CLZ(bitmap));    }⒀  STATIC INLINE UINT32 OsMemLog2(UINT32 size)    {        return (size > 0) ? OsMemFLS(size) : 0;    }    /* Get the first level: f = log2(size). */⒁  STATIC INLINE UINT32 OsMemFlGet(UINT32 size)    {        if (size < OS_MEM_SMALL_BUCKET_MAX_SIZE) {            return ((size >> 2) - 1); /* 2: The small bucket setup is 4. */        }        return (OsMemLog2(size) - OS_MEM_LARGE_START_BUCKET + OS_MEM_SMALL_BUCKET_COUNT);    }    /* Get the second level: s = (size - 2^f) * 2^SLI / 2^f. */⒂  STATIC INLINE UINT32 OsMemSlGet(UINT32 size, UINT32 fl)    {        if ((fl < OS_MEM_SMALL_BUCKET_COUNT) || (size < OS_MEM_SMALL_BUCKET_MAX_SIZE)) {            PRINT_ERR("fl or size is too small, fl = %u, size = %u\n", fl, size);            return 0;        }        UINT32 sl = (size << OS_MEM_SLI) >> (fl - OS_MEM_SMALL_BUCKET_COUNT + OS_MEM_LARGE_START_BUCKET);        return (sl - (1 << OS_MEM_SLI));    }

2、动态内存罕用操作

动态内存治理模块为用户提供初始化和删除内存池、申请、开释动态内存等操作,咱们来剖析下接口的源代码。在剖析下内存操作接口之前,咱们先看下一下罕用的外部接口。

2.1 动态内存外部接口

2.1.1 设置和清理闲暇内存链表标记位

⑴处函数OsMemSetFreeListBit须要2个参数,一个是内存池池头head,一个是闲暇内存链表索引index。当闲暇内存链表上挂载有闲暇内存块时,位图字相应的位须要设置为1。⑴处函数OsMemClearFreeListBit做相同的操作,当闲暇内存链表上不再挂载闲暇内存块时,须要对应的比特位清零。

  STATIC INLINE VOID OsMemSetFreeListBit(struct OsMemPoolHead *head, UINT32 index)  {⑴    head->freeListBitmap[index >> 5] |= 1U << (index & 0x1f);  }  STATIC INLINE VOID OsMemClearFreeListBit(struct OsMemPoolHead *head, UINT32 index)  {⑵    head->freeListBitmap[index >> 5] &= ~(1U << (index & 0x1f));  }

2.1.2 合并内存节点

函数VOID OsMemMergeNode(struct OsMemNodeHead node)用于合并给定节点struct OsMemNodeHead node和它前一个闲暇节点。⑴处把前一个节点的大小加上要合入节点的大小。⑵处获取给定节点的下一个节点,而后执行⑶把它的前一个节点指向给定节点的前一个节点,实现节点的合并。其中宏OS_MEM_NODE_GET_LAST_FLAG用于判断是否最初一个节点,默认为0,能够自行查看下该宏的定义。

STATIC INLINE VOID OsMemMergeNode(struct OsMemNodeHead *node){    struct OsMemNodeHead *nextNode = NULL;⑴  node->ptr.prev->sizeAndFlag += node->sizeAndFlag;⑵  nextNode = (struct OsMemNodeHead *)((UINTPTR)node + node->sizeAndFlag);    if (!OS_MEM_NODE_GET_LAST_FLAG(nextNode->sizeAndFlag)) {⑶      nextNode->ptr.prev = node->ptr.prev;    }}

2.1.3 宰割内存节点

函数VOID OsMemSplitNode(VOID pool, struct OsMemNodeHead allocNode, UINT32 allocSize)用于宰割内存节点,须要三个参数。VOID pool是内存池起始地址,struct OsMemNodeHead allocNode示意从该内存节点调配出须要的内存,UINT32 allocSize是须要调配的内存大小。宰割之后残余的局部,如果下一个节点是闲暇节点,则合并一起。宰割残余的节点会挂载到闲暇内存链表上。

⑴处示意newFreeNode是调配之后残余的闲暇内存节点,设置它的上一个节点为调配的节点,并设置残余内存大小。⑵处调整分配内存的大小,⑶处获取下一个节点,而后执行⑷下一个节点的前一个节点设置为新的闲暇节点newFreeNode。⑸处判断下一个节点是否被应用,如果没有应用,则把下一个节点从链表中删除,而后和闲暇节点newFreeNode合并。⑹处宰割残余的闲暇内存节点挂载到链表上。

STATIC INLINE VOID OsMemSplitNode(VOID *pool, struct OsMemNodeHead *allocNode, UINT32 allocSize){    struct OsMemFreeNodeHead *newFreeNode = NULL;    struct OsMemNodeHead *nextNode = NULL;⑴  newFreeNode = (struct OsMemFreeNodeHead *)(VOID *)((UINT8 *)allocNode + allocSize);    newFreeNode->header.ptr.prev = allocNode;    newFreeNode->header.sizeAndFlag = allocNode->sizeAndFlag - allocSize;⑵  allocNode->sizeAndFlag = allocSize;⑶  nextNode = OS_MEM_NEXT_NODE(&newFreeNode->header);    if (!OS_MEM_NODE_GET_LAST_FLAG(nextNode->sizeAndFlag)) {⑷      nextNode->ptr.prev = &newFreeNode->header;        if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->sizeAndFlag)) {⑸          OsMemFreeNodeDelete(pool, (struct OsMemFreeNodeHead *)nextNode);            OsMemMergeNode(nextNode);        }    }⑹  OsMemFreeNodeAdd(pool, newFreeNode);}

2.1.4 从新申请内存

OsMemReAllocSmaller()函数用于从一个大的内存块里从新申请一个较小的内存,他须要的4个参数别离是:VOID pool是内存池起始地址,UINT32 allocSize是从新申请的内存的大小,struct OsMemNodeHead node是以后须要从新分配内存的内存节点,UINT32 nodeSize是以后节点的大小。⑴设置内存节点selfNode.sizeAndFlag为去除标记后的理论大小,⑵按需宰割节点,⑶宰割后的节点设置已应用标记,实现实现申请内存。

STATIC INLINE VOID OsMemReAllocSmaller(VOID *pool, UINT32 allocSize, struct OsMemNodeHead *node, UINT32 nodeSize){#if (LOSCFG_MEM_WATERLINE == 1)    struct OsMemPoolHead *poolInfo = (struct OsMemPoolHead *)pool;#endif⑴  node->sizeAndFlag = nodeSize;    if ((allocSize + OS_MEM_MIN_LEFT_SIZE) <= nodeSize) {⑵       OsMemSplitNode(pool, node, allocSize);#if (LOSCFG_MEM_WATERLINE == 1)        poolInfo->info.curUsedSize -= nodeSize - allocSize;#endif    }⑶  OS_MEM_NODE_SET_USED_FLAG(node->sizeAndFlag);#if (LOSCFG_MEM_LEAKCHECK == 1)    OsMemLinkRegisterRecord(node);#endif}

2.1.5 合并节点从新申请内存

最初,再来看下函数函数OsMemMergeNodeForReAllocBigger(),用于合并内存节点,重新分配更大的内存空间。它须要5个参数,VOID pool是内存池起始地址,UINT32 allocSize是从新申请的内存的大小,struct OsMemNodeHead node是以后须要从新分配内存的内存节点,UINT32 nodeSize是以后节点的大小,struct OsMemNodeHead *nextNode是下一个内存节点。⑴处设置内存节点的大小为去除标记后的理论大小,⑵把下一个节点从链表上删除,而后合并节点。⑶处如果合并后的节点大小超过须要重新分配的大小,则宰割节点。⑷处把申请的内存节点标记为已应用,实现实现申请内存

STATIC INLINE VOID OsMemMergeNodeForReAllocBigger(VOID *pool, UINT32 allocSize, struct OsMemNodeHead *node,                                                  UINT32 nodeSize, struct OsMemNodeHead *nextNode){⑴  node->sizeAndFlag = nodeSize;⑵  OsMemFreeNodeDelete(pool, (struct OsMemFreeNodeHead *)nextNode);    OsMemMergeNode(nextNode);    if ((allocSize + OS_MEM_MIN_LEFT_SIZE) <= node->sizeAndFlag) {⑶       OsMemSplitNode(pool, node, allocSize);    }⑷  OS_MEM_NODE_SET_USED_FLAG(node->sizeAndFlag);    OsMemWaterUsedRecord((struct OsMemPoolHead *)pool, node->sizeAndFlag - nodeSize);#if (LOSCFG_MEM_LEAKCHECK == 1)    OsMemLinkRegisterRecord(node);#endif}

2.1.6 闲暇内存链表相干操作

动态内存提供了针对闲暇内存链表的几个操作,咱们顺次剖析下这些操作的代码。首先看下函数OsMemFreeListIndexGet,依据内存节点大小获取闲暇内存链表的索引。⑴处先获取一级索引,⑵处获取二级索引,而后计算闲暇链表的索引并返回。

STATIC INLINE UINT32 OsMemFreeListIndexGet(UINT32 size){⑴  UINT32 fl = OsMemFlGet(size);    if (fl < OS_MEM_SMALL_BUCKET_COUNT) {        return fl;    }⑵  UINT32 sl = OsMemSlGet(size, fl);    return (OS_MEM_SMALL_BUCKET_COUNT + ((fl - OS_MEM_SMALL_BUCKET_COUNT) << OS_MEM_SLI) + sl);}

接着看下函数OsMemListAdd,如何把闲暇内存节点插入闲暇内存链表。⑴处获取闲暇链表的第一个节点,如果节点不为空,则把这个节点的前驱节点设置为待插入节点node。⑵处设置待插入节点的前驱、后继节点,而后把该节点赋值给闲暇链表pool->freeList[listIndex]。最初执行⑶处代码,把设置闲暇链表位图字,并设置魔术字。

STATIC INLINE VOID OsMemListAdd(struct OsMemPoolHead *pool, UINT32 listIndex, struct OsMemFreeNodeHead *node){⑴  struct OsMemFreeNodeHead *firstNode = pool->freeList[listIndex];    if (firstNode != NULL) {        firstNode->prev = node;    }⑵  node->prev = NULL;    node->next = firstNode;    pool->freeList[listIndex] = node;⑶  OsMemSetFreeListBit(pool, listIndex);    OS_MEM_SET_MAGIC(&node->header);}

最初,剖析下函数OsMemListDelete如何从闲暇内存链表删除指定的闲暇内存节点。⑴处如果删除的节点是闲暇内存链表的第一个节点,则须要把闲暇链表执行待删除节点的下一个节点。如果下一个节点为空,须要执行⑵革除闲暇链表的位图字。否则执行⑶把下一个节点的前驱节点设置为空。如果待删除节点不是闲暇链表的第一个节点,执行⑷把待删除节点的前驱节点的后续节点设置为待删除节点的后继节点。如果待删除节点不为最初一个节点,须要执行⑸把待删除节点的后继节点的前驱节点设置为待删除节点的前驱节点。最初须要设置下魔术字。

STATIC INLINE VOID OsMemListDelete(struct OsMemPoolHead *pool, UINT32 listIndex, struct OsMemFreeNodeHead *node){⑴  if (node == pool->freeList[listIndex]) {        pool->freeList[listIndex] = node->next;        if (node->next == NULL) {⑵          OsMemClearFreeListBit(pool, listIndex);        } else {⑶          node->next->prev = NULL;        }    } else {⑷      node->prev->next = node->next;        if (node->next != NULL) {⑸          node->next->prev = node->prev;        }    }    OS_MEM_SET_MAGIC(&node->header);}

2.1.7 闲暇内存节点相干操作

动态内存提供了针对闲暇内存的几个操作,如OsMemFreeNodeAdd、OsMemFreeNodeDelete、OsMemFreeNodeGet。

函数OsMemFreeNodeAdd用于把一个闲暇内存节点退出相应的闲暇内存链表上。⑴处调用函数获取闲暇内存链表的索引,而后执行⑵把闲暇内存节点退出闲暇链表。

STATIC INLINE VOID OsMemFreeNodeAdd(VOID *pool, struct OsMemFreeNodeHead *node){⑴  UINT32 index = OsMemFreeListIndexGet(node->header.sizeAndFlag);    if (index >= OS_MEM_FREE_LIST_COUNT) {        LOS_Panic("The index of free lists is error, index = %u\n", index);    }⑵  OsMemListAdd(pool, index, node);}

函数OsMemFreeNodeDelete用于把一个闲暇内存节点从相应的闲暇内存链表上删除。代码较简略,获取闲暇内存链表的索引,而后调用函数OsMemListDelete进行删除。

STATIC INLINE VOID OsMemFreeNodeDelete(VOID *pool, struct OsMemFreeNodeHead *node){    UINT32 index = OsMemFreeListIndexGet(node->header.sizeAndFlag);    OsMemListDelete(pool, index, node);}

函数OsMemFreeNodeGet依据内存池地址和须要的内存大小获取满足大小条件的闲暇内存块。⑴处调用函数获取满足大小条件的内存块,而后执行⑵把获取到的内存块从闲暇内存链表删除,返回内存节点地址。

STATIC INLINE struct OsMemNodeHead *OsMemFreeNodeGet(VOID *pool, UINT32 size){    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;    UINT32 index;⑴  struct OsMemFreeNodeHead *firstNode = OsMemFindNextSuitableBlock(pool, size, &index);    if (firstNode == NULL) {        return NULL;    }⑵  OsMemListDelete(poolHead, index, firstNode);    return &firstNode->header;}

最初,剖析下函数OsMemFindNextSuitableBlock。⑴处依据须要的内存块大小获取一级区间编号,如果申请的内存处于[4,127]区间,执行⑵处记录闲暇内存链表索引。如果须要申请的是大内存,执行⑶处代码。先获取二级区间索引,而后计算出闲暇内存链表的索引值index。这样计算出来的闲暇内存链表下可能并没有挂载闲暇内存块,调用⑷处函数OsMemNotEmptyIndexGet获取挂载闲暇内存块的闲暇内存链表索引值。如果胜利获取到满足大小的闲暇内存块,返回闲暇链表索引值,否则继续执行后续代码。⑹处对闲暇链表位图字进行遍历,循环中的自增变量index对应一级区间编号。如果位图字不为空,执行⑺获取这个位图字对应的最大的闲暇内存链表的索引。

如果执行到⑻处,阐明没有匹配到适合的内存块,返回空指针。⑼处示意存在满足大小的闲暇内存链表,调用函数OsMemFindCurSuitableBlock获取适合的内存块并返回。⑽处标签示意获取到适合的闲暇内存链表索引,返回闲暇内存链表。

STATIC INLINE struct OsMemFreeNodeHead *OsMemFindNextSuitableBlock(VOID *pool, UINT32 size, UINT32 *outIndex){    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;⑴  UINT32 fl = OsMemFlGet(size);    UINT32 sl;    UINT32 index, tmp;    UINT32 curIndex = OS_MEM_FREE_LIST_COUNT;    UINT32 mask;    do {        if (fl < OS_MEM_SMALL_BUCKET_COUNT) {⑵          index = fl;        } else {⑶          sl = OsMemSlGet(size, fl);            curIndex = ((fl - OS_MEM_SMALL_BUCKET_COUNT) << OS_MEM_SLI) + sl + OS_MEM_SMALL_BUCKET_COUNT;            index = curIndex + 1;        }⑷      tmp = OsMemNotEmptyIndexGet(poolHead, index);        if (tmp != OS_MEM_FREE_LIST_COUNT) {⑸          index = tmp;            goto DONE;        }⑹      for (index = LOS_Align(index + 1, 32); index < OS_MEM_FREE_LIST_COUNT; index += 32) {            mask = poolHead->freeListBitmap[index >> 5]; /* 5: Divide by 32 to calculate the index of the bitmap array. */            if (mask != 0) {⑺              index = OsMemFFS(mask) + index;                goto DONE;            }        }    } while (0);⑻  if (curIndex == OS_MEM_FREE_LIST_COUNT) {        return NULL;    }⑼  *outIndex = curIndex;    return OsMemFindCurSuitableBlock(poolHead, curIndex, size);DONE:    *outIndex = index;⑽  return poolHead->freeList[index];}

咱们再详细分析下函数OsMemNotEmptyIndexGet的源码。⑴处依据闲暇内存链表索引获取位图字,⑵处判断闲暇内存链表索引对应的一级内存区间对应的二级小内存区间是否存在满足条件的闲暇内存块。其中index & OS_MEM_BITMAP_MASK对索引只取低5位后,能够把索引值和位图字中的比特位关联起来,比方index为39时,index & OS_MEM_BITMAP_MASK等于7,对应位图字的第7位。表达式~((1 << (index & OS_MEM_BITMAP_MASK)) - 1)则用于示意大于闲暇内存链表索引index的索引值对应的位图字。⑵处的语句执行后,mask就示意闲暇链表索引值大于index的链表索引对应的位图字的值。当mask不为0时,示意存在满足内存大小的闲暇内存块,则执行⑶处代码,其中OsMemFFS(mask)获取位图字中第一个为1的比特位位数,该位对应着挂载闲暇内存块的链表。(index & ~OS_MEM_BITMAP_MASK)对应链表索引的高位,加上位图字位数就计算出挂载着满足申请条件的闲暇内存链表的索引值。

STATIC INLINE UINT32 OsMemNotEmptyIndexGet(struct OsMemPoolHead *poolHead, UINT32 index){⑴  UINT32 mask = poolHead->freeListBitmap[index >> 5]; /* 5: Divide by 32 to calculate the index of the bitmap array. */⑵  mask &= ~((1 << (index & OS_MEM_BITMAP_MASK)) - 1);    if (mask != 0) {⑶      index = OsMemFFS(mask) + (index & ~OS_MEM_BITMAP_MASK);        return index;    }    return OS_MEM_FREE_LIST_COUNT;}

最初,再看下函数OsMemFindCurSuitableBlock。⑴处循环遍历闲暇内存链表上挂载的内存块,如果遍历到的内存块大小大于须要的大小,则执行⑵返回该闲暇内存块。否则返回空指针。

STATIC INLINE struct OsMemFreeNodeHead *OsMemFindCurSuitableBlock(struct OsMemPoolHead *poolHead,                                        UINT32 index, UINT32 size){    struct OsMemFreeNodeHead *node = NULL;⑴  for (node = poolHead->freeList[index]; node != NULL; node = node->next) {        if (node->header.sizeAndFlag >= size) {⑵           return node;        }    }    return NULL;}

2.2 初始化动态内存池

咱们剖析下初始化动态内存池函数UINT32 LOS_MemInit(VOID pool, UINT32 size)的代码。咱们先看看函数参数,VOID pool是动态内存池的起始地址,UINT32 size是初始化的动态内存池的总大小,size须要小于等于*pool开始的内存区域的大小,否则会影响前面的内存区域,还须要大于动态内存池的最小值OS_MEM_MIN_POOL_SIZE。[pool, pool + size]不能和其余内存池抵触。

咱们看下代码,⑴处对传入参数进行校验,⑵处对传入参数进行是否内存对齐校验,如果没有内存对齐会返回错误码。⑶处调用函数OsMemPoolInit()进行内存池初始化,这是初始化内存的外围函数。⑷处开启宏LOSCFG_MEM_MUL_POOL多内存池反对时,才会执行。

UINT32 LOS_MemInit(VOID *pool, UINT32 size){⑴  if ((pool == NULL) || (size <= OS_MEM_MIN_POOL_SIZE)) {        return OS_ERROR;    }⑵  if (((UINTPTR)pool & (OS_MEM_ALIGN_SIZE - 1)) || \        (size & (OS_MEM_ALIGN_SIZE - 1))) {        PRINT_ERR("LiteOS heap memory address or size configured not aligned:address:0x%x,size:0x%x, alignsize:%d\n", \                  (UINTPTR)pool, size, OS_MEM_ALIGN_SIZE);        return OS_ERROR;    }⑶  if (OsMemPoolInit(pool, size)) {        return OS_ERROR;    }#if (LOSCFG_MEM_MUL_POOL == 1)⑷  if (OsMemPoolAdd(pool, size)) {        (VOID)OsMemPoolDeinit(pool);        return OS_ERROR;    }#endif#if OS_MEM_TRACE    LOS_TraceReg(LOS_TRACE_MEM_TIME, OsMemTimeTrace, LOS_TRACE_MEM_TIME_NAME, LOS_TRACE_ENABLE);    LOS_TraceReg(LOS_TRACE_MEM_INFO, OsMemInfoTrace, LOS_TRACE_MEM_INFO_NAME, LOS_TRACE_ENABLE);#endif    OsHookCall(LOS_HOOK_TYPE_MEM_INIT, pool, size);    return LOS_OK;}

咱们持续看下函数OsMemPoolInit()。⑴处设置动态内存池信息结构体struct OsMemPoolHead *poolHead的起始地址和大小,⑵处设置内存池属性设置为锁定、不可扩大。⑶处获取内存池的第一个内存管制节点,而后设置它的大小,该节点大小等于内存池总大小减去内存池池头大小和一个内存节拍板大小。而后再设置该内存节点的上一个节点为内存池的最初一个节点OS_MEM_END_NODE(pool, size)。

⑷处调用宏给节点设置魔术字,而后把内存节点插入到闲暇内存链表中。⑸处获取内存池的尾节点,设置魔术字,而后执行⑹设置尾节点大小为0和设置上一个节点,并设置已应用标记。如果开启调测宏LOSCFG_MEM_WATERLINE,还会有些其余操作,自行浏览即可。

STATIC UINT32 OsMemPoolInit(VOID *pool, UINT32 size){    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;    struct OsMemNodeHead *newNode = NULL;    struct OsMemNodeHead *endNode = NULL;    (VOID)memset_s(poolHead, sizeof(struct OsMemPoolHead), 0, sizeof(struct OsMemPoolHead));⑴  poolHead->info.pool = pool;    poolHead->info.totalSize = size;    poolHead->info.attr &= ~(OS_MEM_POOL_UNLOCK_ENABLE | OS_MEM_POOL_EXPAND_ENABLE); /* default attr: lock, not expand. */⑶  newNode = OS_MEM_FIRST_NODE(pool);    newNode->sizeAndFlag = (size - sizeof(struct OsMemPoolHead) - OS_MEM_NODE_HEAD_SIZE);    newNode->ptr.prev = OS_MEM_END_NODE(pool, size);⑷  OS_MEM_SET_MAGIC(newNode);    OsMemFreeNodeAdd(pool, (struct OsMemFreeNodeHead *)newNode);    /* The last mem node */⑸  endNode = OS_MEM_END_NODE(pool, size);    OS_MEM_SET_MAGIC(endNode);#if OS_MEM_EXPAND_ENABLE    endNode->ptr.next = NULL;    OsMemSentinelNodeSet(endNode, NULL, 0);#else⑹  endNode->sizeAndFlag = 0;    endNode->ptr.prev = newNode;    OS_MEM_NODE_SET_USED_FLAG(endNode->sizeAndFlag);#endif#if (LOSCFG_MEM_WATERLINE == 1)    poolHead->info.curUsedSize = sizeof(struct OsMemPoolHead) + OS_MEM_NODE_HEAD_SIZE;    poolHead->info.waterLine = poolHead->info.curUsedSize;#endif    return LOS_OK;}

2.3 申请动态内存

初始化动态内存池后,咱们能够应用函数VOID LOS_MemAlloc(VOID pool, UINT32 size)来申请动态内存,上面剖析下源码。

⑴处对参数进行校验,内存池地址不能为空,申请的内存大小不能为0。⑵处判断申请的内存大小是否已标记为应用或内存对齐。⑶处调用函数OsMemAlloc(poolHead, size, intSave)申请内存块。

VOID *LOS_MemAlloc(VOID *pool, UINT32 size){#if OS_MEM_TRACE    UINT64 start = HalClockGetCycles();#endif⑴  if ((pool == NULL) || (size == 0)) {        return NULL;    }    if (size < OS_MEM_MIN_ALLOC_SIZE) {        size = OS_MEM_MIN_ALLOC_SIZE;    }    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;    VOID *ptr = NULL;    UINT32 intSave;    MEM_LOCK(poolHead, intSave);    do {⑵      if (OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size)) {            break;        }⑶      ptr = OsMemAlloc(poolHead, size, intSave);    } while (0);    MEM_UNLOCK(poolHead, intSave);#if OS_MEM_TRACE    UINT64 end = HalClockGetCycles();    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_MALLOC, timeUsed);    LOS_MEM_POOL_STATUS poolStatus = {0};    (VOID)LOS_MemInfoGet(pool, &poolStatus);    UINT8 fragment = 100 - poolStatus.maxFreeNodeSize * 100 / poolStatus.totalFreeSize; /* 100: percent denominator. */    UINT8 usage = LOS_MemTotalUsedGet(pool) * 100 / LOS_MemPoolSizeGet(pool); /* 100: percent denominator. */    LOS_Trace(LOS_TRACE_MEM_INFO, (UINTPTR)pool & MEM_POOL_ADDR_MASK, fragment, usage, poolStatus.totalFreeSize,              poolStatus.maxFreeNodeSize, poolStatus.usedNodeNum, poolStatus.freeNodeNum);#endif    OsHookCall(LOS_HOOK_TYPE_MEM_ALLOC, pool, size);    return ptr;}

咱们持续剖析函数OsMemAlloc()。⑴处对申请内存大小加上头结点大小的和进行内存对齐,⑵处从闲暇内存链表中获取一个满足申请大小的闲暇内存块,如果申请失败,则打印错误信息。⑶处如果找到的内存块大于须要的内存大小,则执行宰割操作。⑷处把已调配的内存节点标记为已应用,更新水线记录。⑸返回内存块的数据区的地址,这个是通过内存节点地址加1定位到数据区内存地址实现的。申请内存实现,调用申请内存的函数中能够应用申请的内存了。

STATIC INLINE VOID *OsMemAlloc(struct OsMemPoolHead *pool, UINT32 size, UINT32 intSave){    struct OsMemNodeHead *allocNode = NULL;#if (LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK == 1)    if (OsMemAllocCheck(pool, intSave) == LOS_NOK) {        return NULL;    }#endif⑴  UINT32 allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE);#if OS_MEM_EXPAND_ENABLEretry:#endif⑵  allocNode = OsMemFreeNodeGet(pool, allocSize);    if (allocNode == NULL) {#if OS_MEM_EXPAND_ENABLE        if (pool->info.attr & OS_MEM_POOL_EXPAND_ENABLE) {            INT32 ret = OsMemPoolExpand(pool, allocSize, intSave);            if (ret == 0) {                goto retry;            }        }#endif        PRINT_ERR("---------------------------------------------------"                  "--------------------------------------------------------\n");        MEM_UNLOCK(pool, intSave);        OsMemInfoPrint(pool);        MEM_LOCK(pool, intSave);        PRINT_ERR("[%s] No suitable free block, require free node size: 0x%x\n", __FUNCTION__, allocSize);        PRINT_ERR("----------------------------------------------------"                  "-------------------------------------------------------\n");        return NULL;    }⑶  if ((allocSize + OS_MEM_MIN_LEFT_SIZE) <= allocNode->sizeAndFlag) {        OsMemSplitNode(pool, allocNode, allocSize);    }⑷  OS_MEM_NODE_SET_USED_FLAG(allocNode->sizeAndFlag);    OsMemWaterUsedRecord(pool, OS_MEM_NODE_GET_SIZE(allocNode->sizeAndFlag));#if (LOSCFG_MEM_LEAKCHECK == 1)    OsMemLinkRegisterRecord(allocNode);#endif⑸  return OsMemCreateUsedNode((VOID *)allocNode);}

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的幂。申请的内存大小必须大于最小的申请值OS_MEM_MIN_ALLOC_SIZE。⑵处校验下对齐内存后是否会数据溢出。⑶处计算对齐后须要申请的内存大小,而后判断内存大小数值没有已应用或已对齐标记。⑷处调用函数申请到内存VOID ptr,而后计算出对齐的内存地址VOID alignedPtr,如果二者相等则返回。⑸处计算出对齐内存的偏移值,⑹处获取申请到的内存的头节点,设置已对齐标记。⑺对偏移值设置对齐标记,而后把偏移值保留在内存VOID *alignedPtr的前4个字节里。⑻处从新定向要返回的指针,实现申请对齐的内存。

VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary){#if OS_MEM_TRACE    UINT64 start = HalClockGetCycles();#endif    UINT32 gapSize;⑴  if ((pool == NULL) || (size == 0) || (boundary == 0) || !OS_MEM_IS_POW_TWO(boundary) ||        !OS_MEM_IS_ALIGNED(boundary, sizeof(VOID *))) {        return NULL;    }    if (size < OS_MEM_MIN_ALLOC_SIZE) {        size = OS_MEM_MIN_ALLOC_SIZE;    }⑵  if ((boundary - sizeof(gapSize)) > ((UINT32)(-1) - size)) {        return NULL;    }⑶  UINT32 useSize = (size + boundary) - sizeof(gapSize);    if (OS_MEM_NODE_GET_USED_FLAG(useSize) || OS_MEM_NODE_GET_ALIGNED_FLAG(useSize)) {        return NULL;    }    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;    UINT32 intSave;    VOID *ptr = NULL;    VOID *alignedPtr = NULL;    MEM_LOCK(poolHead, intSave);    do {⑷      ptr = OsMemAlloc(pool, useSize, intSave);        alignedPtr = (VOID *)OS_MEM_ALIGN(ptr, boundary);        if (ptr == alignedPtr) {            break;        }        /* store gapSize in address (ptr - 4), it will be checked while free */⑸      gapSize = (UINT32)((UINTPTR)alignedPtr - (UINTPTR)ptr);⑹      struct OsMemUsedNodeHead *allocNode = (struct OsMemUsedNodeHead *)ptr - 1;        OS_MEM_NODE_SET_ALIGNED_FLAG(allocNode->header.sizeAndFlag);⑺      OS_MEM_SET_GAPSIZE_ALIGNED_FLAG(gapSize);        *(UINT32 *)((UINTPTR)alignedPtr - sizeof(gapSize)) = gapSize;⑻      ptr = alignedPtr;    } while (0);    MEM_UNLOCK(poolHead, intSave);#if OS_MEM_TRACE    UINT64 end = HalClockGetCycles();    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_MEMALIGN, timeUsed);#endif    OsHookCall(LOS_HOOK_TYPE_MEM_ALLOCALIGN, pool, size, boundary);    return ptr;}

2.5 开释动态内存

对申请的内存块应用结束,咱们能够应用函数UINT32 LOS_MemFree(VOID pool, VOID ptr)来开释动静态内存,须要2个参数,VOID pool是初始化过的动态内存池地址。VOID ptr是须要开释的动态内存块的数据区的起始地址,留神这个不是内存管制节点的地址。上面剖析下源码,⑴处对传入的参数先进行校验。⑵处获取校准内存对齐后的实在的内存地址,而后获取内存节拍板地址。⑶处调用函数OsMemFree(pool, ptr)实现内存的开释。

UINT32 LOS_MemFree(VOID *pool, VOID *ptr){#if OS_MEM_TRACE    UINT64 start = HalClockGetCycles();#endif⑴  if ((pool == NULL) || (ptr == NULL) || !OS_MEM_IS_ALIGNED(pool, sizeof(VOID *)) ||        !OS_MEM_IS_ALIGNED(ptr, sizeof(VOID *))) {        return LOS_NOK;    }    UINT32 ret = LOS_NOK;    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;    struct OsMemNodeHead *node = NULL;    UINT32 intSave;    MEM_LOCK(poolHead, intSave);    do {⑵      ptr = OsGetRealPtr(pool, ptr);        if (ptr == NULL) {            break;        }        node = (struct OsMemNodeHead *)((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE);⑶      ret = OsMemFree(poolHead, node);    } while (0);    MEM_UNLOCK(poolHead, intSave);#if OS_MEM_TRACE    UINT64 end = HalClockGetCycles();    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_FREE, timeUsed);#endif    OsHookCall(LOS_HOOK_TYPE_MEM_FREE, pool, ptr);    return ret;}

咱们回过头来,持续看下函数OsGetRealPtr()。⑴获取内存对齐的偏移值,⑵如果偏移值同时标记为已应用和已对齐,则返回谬误。⑶如果偏移值标记为已对齐,则执行⑷去除对齐标记,获取不带标记的偏移值。而后执行⑸,获取内存对齐之前的数据区内存地址。

STATIC INLINE VOID *OsGetRealPtr(const VOID *pool, VOID *ptr){    VOID *realPtr = ptr;⑴  UINT32 gapSize = *((UINT32 *)((UINTPTR)ptr - sizeof(UINT32)));⑵  if (OS_MEM_GAPSIZE_CHECK(gapSize)) {        PRINT_ERR("[%s:%d]gapSize:0x%x error\n", __FUNCTION__, __LINE__, gapSize);        return NULL;    }⑶  if (OS_MEM_GET_GAPSIZE_ALIGNED_FLAG(gapSize)) {⑷      gapSize = OS_MEM_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);            return NULL;        }⑸      realPtr = (VOID *)((UINTPTR)ptr - (UINTPTR)gapSize);    }    return realPtr;}

2.6 从新申请动态内存

咱们还能够应用函数VOID LOS_MemRealloc(VOID pool, VOID ptr, UINT32 size),按指定size大小从新分配内存块,并将原内存块内容拷贝到新内存块。如果新内存块申请胜利,则开释原内存块。该函数须要3个参数,VOID pool为内存池起始地址,VOID *ptr为之前申请的内存地址,UINT32 size为从新申请的内存大小。返回值为新内存块地址,或者返回NULL。上面剖析下源码。

⑴处对参数进行校验,内存池地址不能为空,内存大小不能含有已应用、已对齐标记。⑵处如果传入的内存地址为空,则等价于LOS_MemAlloc()函数。⑶如果传入size为0,等价于函数LOS_MemFree()。⑷处保障申请的内存块大小至多为零碎容许的最小值OS_MEM_MIN_ALLOC_SIZE。⑸处获取内存对齐之前的内存地址,上文已剖析该函数OsGetRealPtr()。⑹处由数据域内存地址计算出内存管制节点node的内存地址,而后执行⑺处函数从新申请内存。

VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size){#if OS_MEM_TRACE    UINT64 start = HalClockGetCycles();#endif⑴  if ((pool == NULL) || OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size)) {        return NULL;    }    OsHookCall(LOS_HOOK_TYPE_MEM_REALLOC, pool, ptr, size);⑵  if (ptr == NULL) {        return LOS_MemAlloc(pool, size);    }⑶  if (size == 0) {        (VOID)LOS_MemFree(pool, ptr);        return NULL;    }⑷  if (size < OS_MEM_MIN_ALLOC_SIZE) {        size = OS_MEM_MIN_ALLOC_SIZE;    }    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;    struct OsMemNodeHead *node = NULL;    VOID *newPtr = NULL;    UINT32 intSave;    MEM_LOCK(poolHead, intSave);    do {⑸      ptr = OsGetRealPtr(pool, ptr);        if (ptr == NULL) {            break;        }⑹      node = (struct OsMemNodeHead *)((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE);        if (OsMemCheckUsedNode(pool, node) != LOS_OK) {            break;        }⑺      newPtr = OsMemRealloc(pool, ptr, node, size, intSave);    } while (0);    MEM_UNLOCK(poolHead, intSave);#if OS_MEM_TRACE    UINT64 end = HalClockGetCycles();    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_REALLOC, timeUsed);#endif    return newPtr;}

持续剖析下函数OsMemRealloc。⑴处解决从新申请的内存小于等于现有的内存的状况,须要调用函数OsMemReAllocSmaller()进行宰割,宰割结束返回(VOID *)ptr即可。如果从新申请更大的内存,则执行⑵处代码获取下一个节点,而后执行⑶解决下一个节点可用且两个节点大小之和大于等于从新申请内存的大小allocSize。执行⑷处的函数,合并节点从新分配内存。

如果间断的节点的大小不满足从新申请内存的大小,则执行⑸处函数从新申请内存。申请胜利后,执行⑹把之前内存的数据复制到新申请的内存区域,复制失败的话,则把新申请的内存开释掉,并返回NULL,退出函数。如果复制胜利,继续执行⑺开释掉之前的节点。

STATIC INLINE VOID *OsMemRealloc(struct OsMemPoolHead *pool, const VOID *ptr,                struct OsMemNodeHead *node, UINT32 size, UINT32 intSave){    struct OsMemNodeHead *nextNode = NULL;    UINT32 allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE);    UINT32 nodeSize = OS_MEM_NODE_GET_SIZE(node->sizeAndFlag);    VOID *tmpPtr = NULL;⑴  if (nodeSize >= allocSize) {        OsMemReAllocSmaller(pool, allocSize, node, nodeSize);        return (VOID *)ptr;    }⑵  nextNode = OS_MEM_NEXT_NODE(node);⑶  if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->sizeAndFlag) &&        ((nextNode->sizeAndFlag + nodeSize) >= allocSize)) {⑷      OsMemMergeNodeForReAllocBigger(pool, allocSize, node, nodeSize, nextNode);        return (VOID *)ptr;    }⑸  tmpPtr = OsMemAlloc(pool, size, intSave);    if (tmpPtr != NULL) {⑹      if (memcpy_s(tmpPtr, size, ptr, (nodeSize - OS_MEM_NODE_HEAD_SIZE)) != EOK) {            MEM_UNLOCK(pool, intSave);            (VOID)LOS_MemFree((VOID *)pool, (VOID *)tmpPtr);            MEM_LOCK(pool, intSave);            return NULL;        }⑺      (VOID)OsMemFree(pool, node);    }    return tmpPtr;}

小结

本文率领大家一起分析了鸿蒙轻内核的动静内存模块的源代码,蕴含动态内存的构造体、动态内存池初始化、动态内存申请、开释等。感激浏览,如有任何问题、倡议,都能够留言给咱们: https://gitee.com/openharmony... 。为了更容易找到鸿蒙轻内核代码仓,倡议拜访 https://gitee.com/openharmony... ,关注Watch、点赞Star、并Fork到本人账户下,谢谢。

点击关注,第一工夫理解华为云陈腐技术~