0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

聊聊FreeRTOS内存管理方案及相关的优化措施

冬至子 来源:剑一二 作者:剑一二 2023-11-13 11:28 次阅读

FreeRTOS 作为一个嵌入式实时操作系统,其运行的环境一般资源有限,特别是其内存资源,可能只有几 M,甚至是几十 KB。针对不同的应用场景,FreeRTOS 源码中提供了 5 种内存管理方案。本文就来聊聊这些内存管理方案以及相关的优化措施。

内存管理相关的代码在 lib/FreeRTOS/portable/MemMang 目录下,从 heap_1.c 到 heap_5.c,文件相互独立,并提供统一接口

void *pvPortMalloc( size_t xWantedSize );
void vPortFree( void *pv );

而 heap 的定义,可以是一块静态分配的数组作为 heap,也可以是指定的一块区域作为 heap,本文就以静态数组为例进行讲解。

#if( configAPPLICATION_ALLOCATED_HEAP == 1)
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif

实际上,内存释放接口并不一定都有效,因为 heap_1 方案是不支持内存释放的。在 heap_1 中调用 pvPortMalloc() 的时候,其具体实现如下所示:

图片

pucAligendHeap: 对齐后的 heap 起始地址。

xNextFreeByte: 记录下一次分配内存的偏移值,实际就是已分配内存的总大小。

pvReturn = pucAlignedHeap + xNextFreeByte;
xNextFreeByte += xWantedSize;

每次分配的时候,返回 pucAligendHeap + xNextFreeByte 的地址,然后更新 xNextFreeByte。当然了,整个分配过程中,还有一些对齐操作。由于分配后的内存不能进行释放,所以这种分配方案适用于不需要频繁申请释放内存的场景。

相对于 heap_1 方案,在 heap_2 中增加了内存释放的函数。其实现是在每一次分配的内存中增加了一些描述信息(也就是多分配一个结构体大小的内存,用于保存描述信息),这样在释放的时候,就可以根据这些信息回收内存。

typedef struct A_BLOCK_LINK {
    struct A_BLOCK_LINK *pxNextFreeBlock;
    size_t xBlockSize;
} BlockLink_t;

static const uint16_t heapSTRUCT_SIZE = (( sizeof( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 )) & ~portBYTE_ALIGNMENT_MASK );

xWantedSize += heapSTRUCT_SIZE;  /*每次分配的时候自动加上描述结构体的大小*/

描述信息结构体中包含了一个链表指针,用于在释放的时候,将该内存加入到空闲链表中,每次分配内存实际就是从空闲链表上进行分配(初始状态,整个链表除了头尾,就一个成员,就是整个空闲的 heap 块)。xBlockSize 用于描述分配内存的大小。

图片

其中,xStart 和 xEnd 是空闲链的头尾节点。初始状态时,整个 heap 就是一个空闲块。分配的时候,从头遍历空闲链,找到第一个满足大小的块,也就是最先匹配原则(first fit),从中分裂出所需的大小,然后将剩余的插入到空闲链中。而释放就是将其插入到空闲链中。

需要注意的是,插入空闲链中,都是升序排列的,也就是说在分配的时候,最先满足的块也是最优的块(best fit),可以减少碎片的产生。从实现来看,heap_2 虽然添加了释放内存函数,但其在插入到空闲链的时候,没有对相邻的块进行合并,所以 heap_2 适用于操作固定大小内存的场景。

heap_3 的方案没有基于 heap_2 进行优化,而是直接使用 libc 库中 malloc() / free() 接口,所以这里就不多做介绍。

事实上,相邻块合并功能是在 heap_4 中引入的。在将空闲块插入到链表的时候,会判断是否有空闲块是相邻的,如果相邻就合并成一个更大的空闲块,就能减少碎片的产生,进而更适用于一般的内存分配和释放场景。注意 heap_4 在插入空闲链的时候,不再是升序排列,而是根据地址大小进行排列,这样便于判断量表中前后两个块是否相邻。

图片

heap_5 相对于 heap_4 方案并没有进行算法上的优化,它添加了一个接口可以指定某个内存块作为 heap。

void vPortDefineHeapRegions( const HeapRegion_t * const pRegions );

在前几种内存管理方案中,除了 heap_3,内存块都是静态分配的。而在 heap_5 中 heap 不再是静态定义的全局变量,而是需要显示指定的一块内存区域作为 heap。这样的好处就是,heap 的来源更加灵活,可以是和运行空间不连续的一块内存,也可以将多个不连续的内存块作为 heap 进行管理。

以上就介绍了 FreeRTOS 中原生的 5 种内存管理方案。可以看出,后面的内存管理方案对前面的管理方案是兼容的,比如 heap_5 可以替代 heap_1, heap_2 和 heap_4 方案。这里可能就有个疑问了,为什么不直接用 heap_5 方案呢?更丰富的功能,意味着复杂的一些实现,在一些简单的场景中,heap_5 分配速度可能没有 heap_1 或者 heap_2 来得快,所以 heap_1,heap_2 等方案也有其存在的意义。

在实际场景中,比如随机的分配和释放,而且分配的大小也不一致,这个时候,一般会选择 heap_4。heap_4 引入了内存合并功能,可以减少内存碎片,但和 heap_2 相比,也把最优匹配的原则去掉了。如下图所示:

图片

当要分配一个 32Bytes 的内存(经过对齐等处理后的大小),按照 heap_4 的分配方案,最先匹配原则,会从 56Bytes 大小的块中分配一个32Bytes 出去,而不是从第二个空闲块,刚刚好是 32Bytes 的块中分配。这样的分配方法就会产生碎片。碎片多了就会导致空闲内存看似很多,但大的内存块已经没有了。

优化的办法也很简单,就是遍历整个空闲链表,找到最优的一个块,其修改方法就是在原来 first fit 的基础上,遍历剩余的链表:

/* first fit */
while ((pxBlock- >xBlockSize < xWantedSize) && (pxBlock- >pxNextFreeBlock != NULL))
{
   pxPreviousBlock = pxBlock;
   pxBlock = pxBlock- >pxNextFreeBlock;
}
/* best fit */
BlockLink_t *pxTmp = pxBlock;
BlockLink_t *pxPreTmp = pxPreviousBlock;while (pxTmp != pxEnd){
   if ((pxTmp- >xBlockSize >= xWantedSize) && (pxTmp- >xBlockSize < pxBlock- >xBlockSize))
  {
       pxBlock = pxTmp;
       pxPreviousBlock = pxPreTmp;
  }
   pxPreTmp = pxTmp;
   pxTmp = pxTmp- >pxNextFreeBlock;
}

最优匹配的引入,不是一定就比原来的方案效果要好,因为遍历整个空闲链表,会导致分配内存的时间变长。这在某些对实时要求较高的环境中就不适应了,比如在一些网络环境中可能会因为超时而导致功能不正常。

其实分配快,内存碎片少的方法有很多,只是他们的实现成本会有所不同,一个算法不可能适用所有的场景,分配方案中也没有哪个比哪个一定更好,找到适合应用场景的就是好的算法。这也许就是 FreeRTOS 中保留了多种分配方案的原因吧。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
收藏 人收藏

    评论

    相关推荐

    第28章 FreeRTOS动态内存管理

    28.4 实验例程说明28.5总结28.1动态内存管理介绍 FreeRTOS支持5种动态内存管理方案
    发表于 09-11 07:15

    FreeRTOS内存管理的算法解析?

    关于FreeRTOS内存管理,有人测试过它给定的算法么?会不会有内存碎片的出现,如果产品一直运行,会不会出现内存崩溃的情况。求证啊。目前用h
    发表于 07-30 11:39

    STM32 FreeRTOS RAM 使用情况及优化方法实用资料分享~

    的一般方法,并给出在 FreeRTOS优化 RAM 使用的方法,也由衷的期望读者在使用其他 RTOS 时,可以通过相似的思路来解决问题。FreeRTOS 的组件包括调度器,内存
    发表于 01-26 14:10

    Freertos关于堆内存管理相关资料分享

    内存管理Malloc、Free防止内存碎片Freertos
    发表于 12-27 08:12

    什么是内存优化?有那些优化措施

    什么是内存优化?有那些优化措施
    发表于 01-14 06:22

    通信设备中内存管理优化

    通过对内存管理的分析,提出了内存优化算法。该算法解决了通信设备中由于大量消息的发送导致内存管理
    发表于 02-21 11:42 22次下载

    FreeRTOS代码剖析之1:内存管理Heap

    内存管理是一个操作系统的重要组成部分之一,所有应用程序都离不开操作系统的内存管理。因此,在剖析FreeRTOS的内核代码之前,前对
    发表于 02-09 05:25 769次阅读
    <b class='flag-5'>FreeRTOS</b>代码剖析之1:<b class='flag-5'>内存</b><b class='flag-5'>管理</b>Heap

    FreeRTOS代码剖析之3:内存管理Heap

    FreeRTOS8.0.1的第三个模型Heap_3,可以说是最容易理解的一个内存管理模型。因为在这个模型里,FreeRTOS直接将标准C库中的malloc()和free()进行加工打
    发表于 02-09 05:30 333次阅读

    嵌入式操作系统FreeRTOS内存如何管理和堆

    嵌入式操作系统FreeRTOS内存管理和堆
    的头像 发表于 01-10 15:17 4337次阅读
    嵌入式操作系统<b class='flag-5'>FreeRTOS</b><b class='flag-5'>内存</b>如何<b class='flag-5'>管理</b>和堆

    FreeRTOS高级篇7---FreeRTOS内存管理分析

    FreeRTOS操作系统将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理函数原型,而不关心这些
    发表于 01-26 17:36 7次下载
    <b class='flag-5'>FreeRTOS</b>高级篇7---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>内存</b><b class='flag-5'>管理</b>分析

    FreeRTOS系列第8篇---FreeRTOS内存管理

    本文介绍内存管理的基础知识,详细源码分析见《 FreeRTOS高级篇7---FreeRTOS内存管理
    发表于 01-26 17:56 17次下载
    <b class='flag-5'>FreeRTOS</b>系列第8篇---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>内存</b><b class='flag-5'>管理</b>

    FreeRTOS的源码下载

    内存管理文件在FreeRTOS\portable\MemMang文件夹下,FreeRTOS提供了五种内存
    的头像 发表于 02-10 11:00 1859次阅读
    <b class='flag-5'>FreeRTOS</b>的源码下载

    freeRTOS源码中内存管理方案

    freeRTOS源码中提供了五种内存管理方案,可以说是很方便了。实际需要使用哪一种,可以根据自己项目的需要进行选择,都是可以的。
    的头像 发表于 02-10 11:11 466次阅读

    FreeRTOS内存管理简介

    ,比如任务创建函数 xTaskCreateStatic(),使用此函数创建任务的时候需要由用户定义任务堆栈,我们不讨论这种静态方法。 使用动态内存管理的时候 FreeRTOS 内核在创建任务、队列、信号量的时候会动态的申请 RA
    的头像 发表于 07-30 10:26 413次阅读

    FreeRTOS内存管理实现

    FreeRTOS是一个为嵌入式系统设计的开源实时操作系统。它提供了一个多任务内核和一系列功能,适合在资源受限的设备上管理实时任务和应用程序。FreeRTOS内存
    的头像 发表于 10-10 16:17 509次阅读
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>内存</b><b class='flag-5'>管理</b>实现