• FreeRTOS 内存管理策略


    目录

    1. FreeRTOS 的核心功能

    2. 为什么 FreeRTOS 要自己实现内存管理

    3. FreeRTOS 的5种内存管理策略

    3.1 Heap_1(只建不删)

    3.2 Heap_2(Heap1_Pro、提供删除,产生碎片)

    内存碎片是怎么出现的

    3.3 Heap_3(传统malloc - free)

    3.4 Heap_4(Heap2_Pro,解决碎片)

    3.5 Heap_5(Heap4_Pro,可管理分隔内存)



    1. FreeRTOS 的核心功能

    2. 为什么 FreeRTOS 要自己实现内存管理

            后续的章节涉及这些内核对象: task、 queue、 semaphores 和 event group 等。为了
    让 FreeRTOS 更容易使用,这些内核对象一般都是动态分配:用到时分配,不使用时释放。

    使用内存的动态管理功能,简化了程序设计:不再需要小心翼翼地提前规划各类对象,简
    API 函数的涉及,甚至可以减少内存的使用。

            内存的动态管理是 C 程序的知识范畴,并不属于 FreeRTOS 的知识范畴,但是它跟
    FreeRTOS 关系是如此紧密,所以我们先讲解它。

            在 C 语言的库函数中,有 mallc、 free 等函数,但是在 FreeRTOS 中,它们不适用:

    • 不适合用在资源紧缺的嵌入式系统中
    • 这些函数的实现过于复杂、占据的代码空间太大(还是因为资源紧缺)
    • 并非线程安全的(thread-safe)
    • 运行有不确定性:每次调用这些函数时花费的时间可能都不相同
    • 内存碎片化(浪费内存)
    • 使用不同的编译器时,需要进行复杂的配置

    注意:我们经常"堆栈"混合着说,其实它们不是同一个东西:

    堆: heap,就是一块空闲的内存,需要提供管理函数

    • malloc:从堆里划出一块空间给程序使用
    • free:用完后,再把它标记为"空闲"的,可以再次使用

    栈: stack,函数调用时局部变量保存在栈中,当前程序的环境也是保存在栈中

    • 可以从堆中分配一块空间用作栈

    3. FreeRTOS 的5种内存管理策略

    3.1 Heap_1(只建不删)

    适用场景:创建的任务、队列、信号量等不需要删除。

            它只实现了 pvPortMalloc,没有实现 vPortFree。如果你的程序不需要删除内核对象,那么可以使用 heap_1:

    • 实现最简单
    • 没有碎片问题(Good)
    • 一些要求非常严格的系统里,不允许使用动态内存,就可以使用 heap_1

    它的实现原理很简单,首先定义一个大数组:

    然后,对于 pvPortMalloc 调用时,从这个数组中分配空间。

    FreeRTOS 在创建任务时,需要 2 个内核对象: task control block(TCB)、 stack。
    使用 heap_1 时,内存分配过程如下图所示:

    • A:创建任务之前整个数组都是空闲的
    • B:创建第 1 个任务之后,蓝色区域被分配出去了
    • C:创建 3 个任务之后的数组使用情况

    3.2 Heap_2(Heap1_Pro、提供删除,产生碎片)

    适用场景:频繁的创建和删除任务,且所创建的任务堆栈都相同此时不会出现碎片化的问题

            Heap_2 之所以还保留,只是为了兼容以前的代码。新设计中不再推荐使用 Heap_2。
    建议使用 Heap_4 来替代 Heap_2,更加高效。

    Heap_2 也是在数组上分配内存,跟 Heap_1 不一样的地方在于:

    • Heap_2 使用最佳匹配算法(best fit)来分配内存
    • 它支持 vPortFree

    最佳匹配算法:

    • 假设 heap 有 3 块空闲内存: 5 字节、 25 字节、 100 字节
    • pvPortMalloc 想申请 20 字节
    • 找出最小的、能满足 pvPortMalloc 的内存: 25 字节
    • 把它划分为 20 字节、 5 字节
    • 返回这 20 字节的地址
    • 剩下的 5 字节仍然是空闲状态,留给后续的 pvPortMalloc 使用

            与 Heap_4 相比, Heap_2 不会合并相邻的空闲内存,所以 Heap_2 会导致严重的"碎片
    化"问题。

            但是,如果申请、分配内存时大小总是相同的,这类场景下 Heap_2 没有碎片化的问
    题。所以它适合这种场景:频繁地创建、删除任务,但是任务的栈大小都是相同的(创建任
    务时,需要分配 TCB 和栈, TCB 总是一样的)。

    虽然不再推荐使用 heap_2,但是它的效率还是远高于 malloc、 free。

    使用 heap_2 时,内存分配过程如下图所示:

    • A:创建了 3 个任务
    • B:删除了一个任务,空闲内存有 3 部分:顶层的、被删除任务的 TCB 空间、被删除任务的 Stack 空间
    • C:创建了一个新任务,因为 TCB、栈大小跟前面被删除任务的 TCB、栈大小一致,所以刚好分配到原来的内存

    内存碎片是怎么出现的

    3.3 Heap_3(传统malloc - free)

            Heap_3 使用标准 C 库里的 malloc、 free 函数,所以堆大小由链接器的配置决定,配
    置项 configTOTAL_HEAP_SIZE 不再起作用。直接用C库的 malloc - free

            C 库里的 malloc、 free 函数并非线程安全的Heap_3 中先暂停 FreeRTOS 的调度器
    再去调用这些函数,使用这种方法实现了线程安全。(用 malloc 和 free 的效率不高),因为heap1和2是在数组中分配内存操作,数组操作比较快和效率高。

            缺点:malloc-free存在碎片问题,一样也存在分配失败的风险。

    3.4 Heap_4(Heap2_Pro,解决碎片)

            跟 Heap_1、 Heap_2 一样, Heap_4 也是使用大数组来分配内存。

            Heap_4 使用首次适应算法(first fit)来分配内存。它还会把相邻的空闲内存合并为一个
    更大的空闲内存,这有助于较少内存的碎片问题。
    (heap2的升级版)

    首次适应算法:

    • 假设堆中有 3 块空闲内存: 5 字节、 200 字节、 100 字节
    • pvPortMalloc 想申请 20 字节
    • 找出第 1 个能满足 pvPortMalloc 的内存: 200 字节
    • 把它划分为 20 字节、 180 字节
    • 返回这 20 字节的地址
    • 剩下的 180 字节仍然是空闲状态,留给后续的 pvPortMalloc 使用

            Heap_4 会把相邻空闲内存合并为一个大的空闲内存,可以减少内存的碎片化问题
    用于这种场景:频繁地分配、释放不同大小的内存。

    Heap_4 的使用过程举例如下:

    • A:创建了 3 个任务
    • B:删除了一个任务,空闲内存有 2 部分:顶层的、被删除任务的 TCB 空间、被删除任务的 Stack 空间合并起来的
    • C:分配了一个 Queue,从第 1 个空闲块中分配空间
    • D:分配了一个 User 数据,从 Queue 之后的空闲块中分配
    • E:释放的 Queue, User 前后都有一块空闲内存
    • F:释放了 User 数据, User 前后的内存、 User 本身占据的内存, 合并为一个大的空闲内存

    Heap_4 执行的时间是不确定的,但是它的效率高于标准库的 malloc、 free。

    3.5 Heap_5(Heap4_Pro,可管理分隔内存)

    Heap_5 分配内存、释放内存的算法跟 Heap_4 是一样的。

    相比于 Heap_4, Heap_5 并不局限于管理一个大数组:它可以管理多块、分隔开的内存

    在嵌入式系统中,内存的地址可能并不连续,这种场景下可以使用 Heap_5。

    既然内存时分隔开的,那么就需要进行初始化:确定这些内存块在哪、多大:

    • 在使用 pvPortMalloc 之前,必须先指定内存块的信息
    • 使用 vPortDefineHeapRegions 来指定这些信息

    怎么指定一块内存?使用如下结构体:



    怎么指定多块内存?使用一个 HeapRegion_t 数组,在这个数组中,低地址在前、高地址在后。

    vPortDefineHeapRegions 函数原型如下:

    把 xHeapRegions 数组传给 vPortDefineHeapRegions 函数,即可初始化 Heap_5。

  • 相关阅读:
    【初学人工智能原理】【3】梯度下降和反向传播:能改(上)
    java: 程序包org.apache.hadoop.hive.metastore不存在
    分布式--Redis持久化策略、主从复制、集群
    【Electron】vue+electron实现图片视频本地缓存
    2309xmake快速编译cpr
    IntelliJ IDEA安装教程
    最优控制理论 九、Bellman动态规划法用于最优控制
    【Electron】vue+electron应用设置菜单
    【踩坑日记】vue项目ie打不开报错SCRIPT1006:缺少‘)‘
    2023年数维杯数学建模A题河流-地下水系统水体污染研究求解全过程文档及程序
  • 原文地址:https://blog.csdn.net/Outside_/article/details/132805454