改进 LiteOS 中物理内存分配算法

上一篇文章写了通过系统调用开辟一块内存,那么问题来了,你是怎么开辟的内存?

这篇文章我们来探索一下鸿蒙内核开辟内存的过程并尝试改进liteos物理内存分配算法。

首先回顾一下上一次是怎么开辟的内存:添加一个系统调用

用到了LOS_MemAlloc函数

LITE_OS_SEC_TEXT VOID *LOS_MemAlloc(VOID *pool, UINT32 size)

{

    VOID *ptr = NULL;

    UINT32 intSave;

/*检查内存池指针和需要分配的大小是否合理*/

    if ((pool == NULL) || (size == 0)) {

        return (size > 0) ? OsVmBootMemAlloc(size) : NULL;

    }

/*内存分配不被打断*/

    MEM_LOCK(intSave);

    do {

        if (OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size)) {

            break;

        }

        ptr = OsMemAllocWithCheck(pool, size, intSave);

    } while (0);

#ifdef LOSCFG_MEM_RECORDINFO

    OsMemRecordMalloc(ptr, size);

#endif

    MEM_UNLOCK(intSave);

    return ptr;

}

你可以看到,在这个函数里面有一个OsMemAllocWithCheck,通过检查的内存分配

STATIC INLINE VOID *OsMemAllocWithCheck(VOID *pool, UINT32 size, UINT32 intSave)

{

    /*动态内存节点*/

    LosMemDynNode *allocNode = NULL;

    /*开辟空间大小*/

    UINT32 allocSize;

    /*内存池信息*/

    LosMemPoolInfo *poolInfo = (LosMemPoolInfo *)pool;

    /*内存池第一个节点*/

    const VOID *firstNode = (const VOID *)((UINT8 *)OS_MEM_HEAD_ADDR(pool) + OS_DLNK_HEAD_SIZE);

    INT32 ret;

    /*内存开辟合法性检查*/

    if (OsMemAllocCheck(pool, intSave) == LOS_NOK) {

        return NULL;

    }

    /*地址对齐*/

    allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE);

    if (allocSize == 0) {

        return NULL;

    }

retry:

    /*找到合适的空闲内存块*/

    allocNode = OsMemFindSuitableFreeBlock(pool, allocSize);

    /*没找到*/

    if (allocNode == NULL) {

        if (poolInfo->flag & MEM_POOL_EXPAND_ENABLE) {

            ret = OsMemPoolExpand(pool, allocSize, intSave);

            if (ret == 0) {

                goto retry;

            }

        }

        PRINT_ERR("---------------------------------------------------"

                  "--------------------------------------------------------\n");

        MEM_UNLOCK(intSave);

        OsMemInfoPrint(pool);

        MEM_LOCK(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_NODE_HEAD_SIZE + OS_MEM_ALIGN_SIZE) <= allocNode->selfNode.sizeAndFlag) {

        OsMemSplitNode(pool, allocNode, allocSize);

    }

    /*从可分配的内存链表里面删除*/

    OsMemListDelete(&allocNode->selfNode.freeNodeInfo, firstNode);

    /*给这块内存设置taskID和其他属性*/

    OsMemSetMagicNumAndTaskID(allocNode);

    OS_MEM_NODE_SET_USED_FLAG(allocNode->selfNode.sizeAndFlag);

    if ((pool == (VOID *)OS_SYS_MEM_ADDR) || (pool == (VOID *)m_aucSysMem0)) {

        OS_MEM_ADD_USED(OS_MEM_NODE_GET_SIZE(allocNode->selfNode.sizeAndFlag), OS_MEM_TASKID_GET(allocNode));

    }

    OsMemNodeDebugOperate(pool, allocNode, size);

    return (allocNode + 1);

}

可以看到,对于内存块的分配部分,这里面的核心部分就是OsMemFindSuitableFreeBlock

STATIC INLINE LosMemDynNode *OsMemFindSuitableFreeBlock(VOID *pool, UINT32 allocSize)

{

    LOS_DL_LIST *listNodeHead = NULL;

    LosMemDynNode *tmpNode = NULL;

    /*获取可以用的内存大小*/

    UINT32 maxCount = (LOS_MemPoolSizeGet(pool) / allocSize) << 1;

    UINT32 count;

#ifdef LOSCFG_MEM_HEAD_BACKUP

    UINT32 ret = LOS_OK;

#endif

    /*顺着往下找可用的空间*/

    for (listNodeHead = OS_MEM_HEAD(pool, allocSize); listNodeHead != NULL;

         listNodeHead = OsDLnkNextMultiHead(OS_MEM_HEAD_ADDR(pool), listNodeHead)) {

        count = 0;

        LOS_DL_LIST_FOR_EACH_ENTRY(tmpNode, listNodeHead, LosMemDynNode, selfNode.freeNodeInfo) {

            if (count++ >= maxCount) {

                PRINT_ERR("[%s:%d]node: execute too much time\n", __FUNCTION__, __LINE__);

                break;

            }

#ifdef LOSCFG_MEM_HEAD_BACKUP

            if (!OsMemChecksumVerify(&tmpNode->selfNode)) {

                PRINT_ERR("[%s]: the node information of current node is bad !!\n", __FUNCTION__);

                OsMemDispCtlNode(&tmpNode->selfNode);

                ret = OsMemBackupRestore(pool, tmpNode);

            }

            if (ret != LOS_OK) {

                break;

            }

#endif

            if (((UINTPTR)tmpNode & (OS_MEM_ALIGN_SIZE - 1)) != 0) {

                LOS_Panic("[%s:%d]Mem node data error:OS_MEM_HEAD_ADDR(pool)=%p, listNodeHead:%p,"

                          "allocSize=%u, tmpNode=%p\n",

                          __FUNCTION__, __LINE__, OS_MEM_HEAD_ADDR(pool), listNodeHead, allocSize, tmpNode);

                break;

            }

            if (tmpNode->selfNode.sizeAndFlag >= allocSize) {

                return tmpNode;

            }

        }

    }

    return NULL;

}

在寻找可用空间的时候,我们使用的是:OS_MEM_HEAD和OsDLnkNextMultiHead

其中,OS_MEM_HEAD的定义是这样的:

#define OS_MEM_HEAD(pool, size)  OsDLnkMultiHead(OS_MEM_HEAD_ADDR(pool), size)

对于OsDLnkMultiHead:

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);

}

首先,在不改变内存调用代码的时候,执行内存开辟任务:

#include<stdio.h>

#include<syscall.h>

int main()

{

  for(int i=1;i<6;i++)

  {

    syscall(SYS_wzsyscall,50);

    syscall(SYS_wzsyscall,2000);

  }

  return 0;

}

得到的结果是:

下面开始修改,首先在memory.c文件里面变量定义的部分添加变量

//mem test to count node

int memNodeCount=0;

然后找到函数OsMemFindSuitableFreeBlock

对于修改之后的OsMemFindSuitableFreeBlock大家可以复制这段代码:

STATIC INLINE LosMemDynNode *OsMemFindSuitableFreeBlock(VOID *pool, UINT32 allocSize)

{
    LOS_DL_LIST *listNodeHead = NULL;

    LosMemDynNode *tmpNode = NULL;

    UINT32 maxCount = (LOS_MemPoolSizeGet(pool) / allocSize) << 1;

    UINT32 count;

#ifdef LOSCFG_MEM_HEAD_BACKUP

    UINT32 ret = LOS_OK;

#endif

    for (listNodeHead = OS_MEM_HEAD(pool, allocSize); listNodeHead != NULL;

         listNodeHead = OsDLnkNextMultiHead(OS_MEM_HEAD_ADDR(pool), listNodeHead)) {

        count = 0;

        LOS_DL_LIST_FOR_EACH_ENTRY(tmpNode, listNodeHead, LosMemDynNode, selfNode.freeNodeInfo) {

              memNodeCount++;//edit new

            if (count++ >= maxCount) {

                PRINT_ERR("[%s:%d]node: execute too much time\n", __FUNCTION__, __LINE__);

                break;

            }

#ifdef LOSCFG_MEM_HEAD_BACKUP

            if (!OsMemChecksumVerify(&tmpNode->selfNode)) {

                PRINT_ERR("[%s]: the node information of current node is bad !!\n", __FUNCTION__);

                OsMemDispCtlNode(&tmpNode->selfNode);

                ret = OsMemBackupRestore(pool, tmpNode);

            }

            if (ret != LOS_OK) {

                break;

            }

#endif
            if (((UINTPTR)tmpNode & (OS_MEM_ALIGN_SIZE - 1)) != 0) {

                LOS_Panic("[%s:%d]Mem node data error:OS_MEM_HEAD_ADDR(pool)=%p, listNodeHead:%p,"

                          "allocSize=%u, tmpNode=%p\n",

                          __FUNCTION__, __LINE__, OS_MEM_HEAD_ADDR(pool), listNodeHead, allocSize, tmpNode);

                break;

            }

            if (tmpNode->selfNode.sizeAndFlag >= allocSize) {

                if(allocSize == 2016 || allocSize == 72){ // edit new

                     PRINTK("count=%d,memNodeCount=%d, tmpNode=%p, allocSize=%d,tmpNode->selfNode.sizeAndFlag=%d\n",count,memNodeCount, tmpNode, allocSize, tmpNode->selfNode.sizeAndFlag);

             }

                return tmpNode;

            }

        }

    }

    return NULL;
}

测试代码如下:

#include<stdio.h>

#include<syscall.h>

int main()

{

  for(int i=1;i<6;i++)

  {

    //printf("memorytest main......\n");

    syscall(SYS_wzsyscall,54);

    syscall(SYS_wzsyscall,2000);

  }

  return 0;

}

结果如下:

可以看到,memNodeCount不是每次都等于一,也就是说,不是一次就能找到合适的内存块。

好的,下面开始修改,best-fit修改成good-fit提升查找的速度:其实就是不要循环找那么多次了直接找个不大不小的差不多能用就行(不严谨的修改,只是为了简单测试运行逻辑)

使用到的文件和路径如下:

根据我们的一贯作风,还是分别修改.c文件和.h文件,首先修改.c文件

如果没有碰着边界,那么就直接index++,相当于不用在这一块找最接近的大小了,直接找一块更大的但又不是大太多的用。

代码如下:

//new good-fit func

LITE_OS_SEC_TEXT_MINOR LOS_DL_LIST *OsDLnkMultiHead_edit(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;

    }else if (index != OS_MAX_MULTI_DLNK_LOG2 && index !=OS_MIN_MULTI_DLNK_LOG2) {

      index++;

    }

    return dlinkHead->listHead + (index - OS_MIN_MULTI_DLNK_LOG2);

}

下面修改相应的.h文件:

extern LOS_DL_LIST *OsDLnkMultiHead_edit(VOID *headAddr, UINT32 size);

使用工具改好了,那么该如何用呢?

下面就需要修改相应调用的部分了:

#define OS_MEM_HEAD_EDIT(pool, size)  OsDLnkMultiHead_edit(OS_MEM_HEAD_ADDR(pool), size)

下面修改调用部分:

测试结果

可以看到每次的memNodeCount都是1,和预期一致

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: LiteOS是一个嵌入式操作系统,其内存管理算法通常是基于内存池的。这意味着系统在启动时会分配一个固定大小的内存池,并在运行过程使用这个内存池来分配内存给不同的应用程序和系统进程。 在内存分配方面,LiteOS使用的是链表动态分配算法。这意味着系统会维护一个链表,其包含可用内存块的信息。当应用程序需要分配内存时,系统会在链表查找第一个足够大的内存块,然后将其分配给应用程序。这种方法的优点在于可以有效地利用内存,避免内存碎片的产生。 LiteOS还提供了垃圾回收功能,可以回收应用程序不再使用的内存。这通常是通过一种称为“标记-清除”的算法来实现的。首先,系统会找出所有正在使用的内存块,然后将它们标记为“可用”。接下来,系统会扫描整个内存池,并清除所有未被标记的内存块,使其成为可用内存。这样,就可以有效地回收应用程序不再使用的内存。 总的来说 ### 回答2: liteos_m是一款轻量级的操作系统,其内存管理算法采用了基于链表的动态分配和释放的方法。 liteos_m的内存管理主要包括两个方面:内存分配和内存释放。其,内存分配使用了链表数据结构,通过维护一个空闲链表和一个已分配链表来管理可用的内存块。当需要分配内存时,liteos_m会从空闲链表找到一个合适大小的内存块,并将其从空闲链表移除,然后将其加入到已分配链表。如果空闲链表没有合适大小的内存块,则需要进行内存碎片整理或者向操作系统申请更多内存。 对于内存释放,liteos_m会将需要释放的内存块从已分配链表移除,并将其重新加入到空闲链表,以便下次分配时使用。此外,为了提高内存的利用率和管理效率,liteos_m还会对连续的空闲内存块进行合并,以减少内存碎片。 总的来说,liteos_m的内存管理算法主要是通过链表数据结构来管理内存块的分配和释放,通过动态维护空闲链表和已分配链表,实现了对内存的高效管理。这种方式既能够灵活地满足多种内存需求,又能够提高内存的利用率和系统的性能。 ### 回答3: liteos_m的内存管理算法原理是基于分段算法和垃圾回收算法。 在liteos_m,内存分为多个段,每个段大小相等且固定。每个段可以分配给不同的任务或对象使用。分段算法的优势是可以避免内存碎片的产生,提高内存的利用率。 在liteos_m,还引入了垃圾回收算法,这是为了解决内存已经不再使用的对象或任务所占用的内存无法被释放的问题。垃圾回收算法通过定期检查内存的对象和任务的引用计数,当引用计数为0时,表示该对象或任务已经不再需要,可以释放其占用的内存。通过垃圾回收算法,可以更好地管理和释放内存,避免内存泄露的问题。 总结来说,liteos_m的内存管理算法原理是通过分段算法来管理内存的分配和释放,同时结合垃圾回收算法来管理内存不再使用的对象和任务的释放,以提高内存的利用率和避免内存泄露的问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值