• FreeRTOS个人笔记-内存管理


    根据个人的学习方向,学习FreeRTOS。由于野火小哥把FreeRTOS讲得比较含蓄,打算在本专栏尽量细化一点。作为个人笔记,仅供参考或查阅。

    配套资料:FreeRTOS内核实现与应用开发实战指南、野火FreeRTOS配套视频源码、b站野火FreeRTOS视频。搭配来看更佳哟!!!

    内存管理

    FreeRTOS 的 V9.0.0 版本为我们提供了 5 种内存管理算法,分别是 heap_1.c、 heap_2.c、 heap_3.c、 heap_4.c、 heap_5.c,源文件存放于FreeRTOS\Source\portable\MemMang 路径下,在使用的时候选择其中一个添加到我们的工程中去即可。

    内存申请函数就是用于申请一块用户指定大小的内存空间,当系统管理的内存空间满足用户需要的大小的时候,就能申请成功,并且返回内存空间的起始地址。

    heap1.c:所有内存管理方案中最简单的一个。


    只能申请内存而不能进行内存释放,并且申请内存的时间是一个常量。
    因为不允许内存释放,就不会产生内存碎片而导致系统崩溃。缺点是内存利用率不高,即使该内存只使用一次,也无法让系统回收重新利用。

    heap1.c 方案具有以下特点:
    1、 用于从不删除任务、队列、信号量、互斥量等的应用程序(实际上大多数使用
    FreeRTOS 的应用程序都符合这个条件) 。
    2、 函数的执行时间是确定的并且不会产生内存碎片。
    常用函数:
    *pvPortMalloc( size_t xWantedSize )         动态申请内存,如*pvPortMalloc( 1024 )
    vPortInitialiseBlocks()                    初始化静态局部变量 xNextFreeByte,表示内存没有被申请。
    xPortGetFreeHeapSize()                    获取当前未分配的内存堆大小

    heap2.c

    采用一种最佳匹配算法,支持释放申请的内存,但是它不能把相邻的两个小的内存块合成一个大的内存块,对于每次申请内存大小都比较固定的。
    由于在释放内存时不会将相邻的内存块合并,所以这可能造成内存碎片。
    假设用户先申请 128 字节内存,然后释放,此时系统释放的 128 字节内存可以重复被利用; 
    如果用户再接着申请 64k 的字节内存,那么一个本来 128 字节的大块就会被分为两个 64 字节的小块,如果这种情况经常发生,就会导致每个空闲块都可能很小,
    最终在申请一个大块时就会因为没有合适的空闲内存块而申请失败,这并不是因为总的空闲内存不足,而是无法申请到连续可以的大块内存。

    vPortFree():向内存释放函数中传入要释放的内存地址,那么系统会自动向前索引到对应链表节点, 并且取出这块内存块的信息,将这个节点插入到空闲内存块链表中,将这个内存块归还给系统。

    heap_2.c 方案具有以下特点:
    1. 可以用在那些反复的删除任务、队列、信号量、等内核对象且不担心内存碎片的
    应用程序。
    2. 如果我们的应用程序中的队列、任务、信号量、 等工作在一个不可预料的顺序,
    这样子也有可能会导致内存碎片。
    3. 具有不确定性,但是效率比标准 C 库中的 malloc 函数高得多
    4. 不能用于那些内存分配和释放是随机大小的应用程序。

    常用函数:
    *pvPortMalloc( size_t xWantedSize )         动态申请内存,如*pvPortMalloc( 1024 )
    vPortFree()                                释放内存

    heap3.c

    只是简单的封装了标准 C 库中的 malloc()和 free()函数, 并且能满足常用的编译器。 
    重新封装后的 malloc()和 free()函数具有保护功能,采用的封装方式是操作内存前挂起调度器、完成后再恢复调度器。

    heap_3.c 方案具有以下特点:
    1、 需要链接器设置一个堆, malloc()和 free()函数由编译器提供。
    2、 具有不确定性。
    3、 很可能增大 RTOS 内核的代码大小。

    常用函数:
    *pvPortMalloc( size_t xWantedSize )         动态申请内存,如*pvPortMalloc( 1024 )
    vPortFree()                                释放内存

    heap4.c

    采用一种最佳匹配算法,支持释放申请的内存,能把相邻的两个小的内存块合成一个大的内存块,减少内存碎片。
    heap_4.c 方案的空闲内存块也是以单链表的形式连接起来的,空闲块链表不是以内存块大小进行排序的,而是以内存块起始地址大小排序,内存地址小的在前,地址大的在后。

    heap_4.c 方案具有以下特点:
    1、可用于重复删除任务、队列、信号量、互斥量等的应用程序
    2、 可用于分配和释放随机字节内存的应用程序, 但并不像 heap2.c 那样产生严重的内
    存碎片。
    3、 具有不确定性,但是效率比标准 C 库中的 malloc 函数高得多。

    heap_4.c 方案的内存申请函数与 heap_2.c 方案的内存申请函数大同小异,同样是从链表头 xStart 开始遍历查找合适的内存块,如果某个空闲内存块的大小能容得下用户要申请的内存,
    则将这块内存取出用户需要内存空间大小的部分返回给用户,剩下的内存块组成一个新的空闲块,按照空闲内存块起始地址大小顺序插入到空闲块链表中,内存地址小的在前,内存地址大的在后。
    在插入到空闲内存块链表的过程中,系统还会执行合并算法将地址相邻的内存块进行合并:判断这个空闲内存块是相邻的空闲内存块合并成一个大内存块,如果可以则合并。
    vPortFree():向内存释放函数中传入要释放的内存地址,那么系统会自动向前索引到对应链表节点, 并且取出这块内存块的信息,将这个内存块插入到空闲内存块链表中,在内存块插入过程中会执行合并算法。
    最后是将这个内存块标志为“空闲” (内存块节点的 xBlockSize 成员变量最高位清 0)、再更新未分配的内存堆大小。

    常用函数:
    *pvPortMalloc( size_t xWantedSize )         动态申请内存,如*pvPortMalloc( 1024 )
    vPortFree()                                释放内存
    xPortGetFreeHeapSize()                     可以知道还剩下多少内存没有使用, 但是并不包括内存碎片。 

    heap5.c

    采用最佳匹配算法和合并算法,并且允许内存堆跨越多个非连续的内存区,也就是允许在不连续的内存堆中实现内存分配。
    在内存初始化未完成前不允许使用内存分配和释放函数,调用 vPortDefineHeapRegions()函数来实现系统管理的内存初始化。

    vPortDefineHeapRegions()实例

    1. /* 在内存中为内存堆分配两个内存块。
    2. 第一个内存块大小为 0x10000 字节,起始地址为 0x80000000,
    3. 第二个内存块大小为 0xa0000 字节,起始地址为 0x90000000。
    4. 起始地址为 0x80000000 的内存块的起始地址更低,因此放到了数组的第一个位置。*/
    5. const HeapRegion_t xHeapRegions[] = {
    6. { ( uint8_t * ) 0x80000000UL, 0x10000 },
    7. { ( uint8_t * ) 0x90000000UL, 0xa0000 },
    8. { NULL, 0 } /* 数组结尾 */
    9. };
    10. vPortDefineHeapRegions( xHeapRegions ); // 向函数 vPortDefineHeapRegions()传递形参

    系统会以一个空闲内存块链表的数据结构记录这些空闲内存,链表以 xStart 节点开头,以 pxEnd 指针指向的位置结束。

    常用函数:
    vPortDefineHeapRegions( xHeapRegions )    初始化内存
    *pvPortMalloc( size_t xWantedSize )         动态申请内存,如*pvPortMalloc( 1024 )
    vPortFree()                                释放内存

    小结:

    造成内存碎片的原由:
    分配内存函数里包含的字节对齐,会产生小的内存碎片。
    如果没有释放内存函数,会产生大的内存碎片。

    文件优点 缺点
    heap_1.c 分配简单,时间确定只分配、不回收
    heap_2.c动态分配、最佳匹配碎片、时间不定
    heap_3.c调用标准库函数 速度慢、时间不定
    heap_4.c 相邻空闲内存可合并 可解决碎片问题、时间不定
    heap_5.c在heap_4基础上支持分隔的内存块可解决碎片问题、时间不定

  • 相关阅读:
    Ubuntu系统中tree的用法
    Java配置35-搭建Gitlab服务器
    【RTOS训练营】I2C和UART知识和预习安排 + 晚课提问
    【C++】STL——vector(万字详解)
    ns-3-model-library wifi 浅析_ns-3wifi部分解析_ns-3网络模拟器wifi部分文档分析_Part3
    惠普战99移动工作站: 第十三代英特尔酷睿处理器和惠普一站式AI应用开发方案的完美融合
    不同的字符串之间的转换
    详解升讯威在线客服系统前端多国语言实现技术:原生支持葡文、印尼文、土耳其文、俄文
    PCA问题汇总
    CSS的列表
  • 原文地址:https://blog.csdn.net/weixin_47077788/article/details/126060178