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

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

3天内不再提示

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

黄工的嵌入式技术圈 来源:黄工的嵌入式技术圈 作者:黄工的嵌入式技术 2020-01-10 15:17 次阅读

编辑:黄工 公众号:strongerHuang 素材来源:FreeRTOS网站 + 网络 前两年FreeRTOS被亚马逊收购之后,变化不大,应该是在规划物联网这一块。 前段时间FreeRTOS官网的界面发生了变化,接下来可能会有大的动作,感兴趣的朋友可以去看一下。 网址:

https://www.freertos.org

回读之前文章:谈谈FreeRTOS_V10版本FreeRTOS更新至V10.2.1 言归正传,回来说FreeRTOS的内存管理和堆的问题。从 V9.0.0 开始,FreeRTOS 应用程序可以完全静态分配,这意味着无需包含堆内存管理器。 FreeRTOS内存管理地址:
https://www.freertos.org/a00111.html 一、拓展知识动态内存分配及其与 FreeRTOS 的关联性:要让 FreeRTOS 对象(如任务、队列、信号量和事件组)变得尽可能易于使用,这些内核对象不是在编译时静态分配,而是在运行时动态分配。每次创建内核对象时,FreeRTOS 都会分配 RAM

每次删除内核对象时,都会释放 RAM。此策略可减少设计和规划工作量,简化了 API,并最大程度地减少 RAM 开销。

动态内存分配是一个 C 语言编程概念。它不是特定于 FreeRTOS 或多任务处理的概念。它与 FreeRTOS 相关,因为内核对象是动态分配的,通用编译器提供的动态内存分配方案并非始终适合实时应用程序。

可以使用标准 C 语言库函数 malloc() 和 free() 分配内存,但由于以下一个或多个原因,这些函数可能不适合或不适用:

•它们并非始终适用于小型嵌入式系统。•它们的实现可能相当大,占用宝贵的代码空间。•它们极少是线程安全的。•它们不是确定性的。执行函数所花的时间量将因调用不同而异。•它们可能会碎片化。如果堆中的可用 RAM 被拆分为若干较小且彼此分离的块,则认为堆已碎片化。当堆已碎片化时,如果堆中没有一个可用块的大小足以包含某个数据块,则尝试分配此数据块时将失败,即使堆中所有单独块的总大小比无法分配的数据块大小大很多倍,也是如此。•它们可能使链接器配置变得更复杂。•如果允许堆空间增长而占用了其他变量使用的内存,则这些函数可能成为一些难以调试的错误的来源。 二、动态内存分配的选项

早期版本的 FreeRTOS 使用内存池分配方案,在编译时预分配由不同大小的内存块组成的池,然后由内存分配函数返回。虽然这是实时系统中常用的方案,但它产生了大量支持请求。由于该方案使用 RAM 的效率无法满足非常小型的嵌入式系统的需要,因此已被放弃。

FreeRTOS 现在将内存分配视为可移植层的一部分(而不是核心代码库的一部分)。这样做的原因是我们已认识到嵌入式系统具有变化多端的动态内存分配和计时要求。单个动态内存分配算法仅适用于一部分应用程序。此外,通过从核心代码库中删除动态内存分配,应用程序编写者可以适时提供其自己的特定实现。

当 FreeRTOS 需要 RAM 时,它调用 pvPortMalloc() 而不是 malloc()。释放 RAM 时,内核调用 vPortFree(),而不是 free()。pvPortMalloc() 与标准 C 语言库函数 malloc() 具有相同的原型。vPortFree() 与标准 C 语言库函数 free() 具有相同的原型。

pvPortMalloc() 和 vPortFree() 是公共函数,因此,也可以从应用程序代码中调用它们。

FreeRTOS 附带了 pvPortMalloc() 和 vPortFree() 的五个示例实现,本指南将一一介绍。FreeRTOS 应用程序可以使用其中一个示例实现或提供自己的实现。

这五个示例在 heap_1.c、heap_2.c、heap_3.c、heap_4.c 和 heap_5.c 源文件中定义,这些源文件位于FreeRTOS/Source/portable/MemMang 目录中。

三、5种内存分配实现

heap_1:最简单,不允许释放内存。

heap_2:允许释放内存,但不能合并相邻的空闲块。

heap_3:简单包装标准的malloc()和free()以确保线程安全。

heap_4:合并相邻的空闲块以避免碎片。包括绝对地址放置选项。

heap_5:按照heap_4,具有跨多个不相邻的内存区域扩展堆的能力。

Heap_1

它常用于小型专用嵌入式系统,以便仅在启动计划程序之前创建任务和其他内核对象。在应用程序开始执行任何实时功能之前,内核动态分配内存,并且内存在应用程序的生命周期内保持已分配状态。这意味着所选分配方案不必考虑任何更复杂的内存分配问题(例如确定性和碎片化),而是可以考虑如代码大小和简单性等属性。

Heap_1.c 实现 pvPortMalloc() 的一个非常基本的版本。它不实现 vPortFree()。从不删除任务或其他内核对象的应用程序可以使用 heap_1。

某些商业关键和安全关键型系统可能禁止动态内存分配,这些系统也可能能够使用 heap_1。由于存在与非确定性、内存碎片化以及分配失败等相关的不确定性,因此,关键系统通常禁止动态内存分配,但 heap_1 始终是确定性的且无法对内存进行碎片化。

当调用 pvPortMalloc() 时,heap_1 分配方案将一个简单的数组细分成更小的块。此数组称为 FreeRTOS堆。

数组的总大小(以字节为单位)由定义 configTOTAL_HEAP_SIZE 在 FreeRTOSConfig.h 中设置。以这种方式定义大型数组可能让应用程序看起来会消耗大量 RAM,甚至从数组中分配任何内存之前就是如此。

每个创建的任务都要求从堆中分配一个任务控制块 (TCB) 和一个堆栈。

下图显示了在创建任务时 heap_1 如何细分简单的数组。每次创建任务时,都会从 heap_1 数组分配 RAM。

A 显示创建任何任务之前的数组。整个数组都可用。

B 显示已创建一个任务后的数组。

C 显示已创建三个任务后的数组。

Heap_2

Heap_2 包含在 FreeRTOS 发行版中以保持向后兼容性。建议不要用于新设计,而是考虑使用 heap_4,因为其中提供了更多功能。

也可以使用 Heap_2.c,但要细分由 configTOTAL_HEAP_SIZE 确定大小的数组。它使用最适合算法分配内存。与 heap_1 不同,它允许释放内存。再次说明,数组是静态声明的,因此应用程序看起来会消耗大量RAM,甚至在从数组中分配任何内存之前就是如此。

最适合算法可确保 pvPortMalloc() 使用的可用内存块在大小方面与所要求的字节数最接近。例如,考虑以下情形:

• 堆包含三个可用内存块,大小分别为 5 字节、25 字节和 100 字节。 • 调用 pvPortMalloc() 以请求 20 字节的 RAM。

适合所请求字节数的最小可用 RAM 块是 25 字节块,因此,pvPortMalloc() 将 25 字节块拆分成一个 20 字节块和一个 5 字节块,然后返回一个指向 20 字节块的指针。(上面是过于简化了,因为 heap_2 要存储堆区域中块大小的信息,因此两个拆分块的总和实际上将小于 25。) 新的 5 字节块保持可用于将来对pvPortMalloc() 的调用。

与 heap_4 不同,heap_2 不将相邻的可用块合并为单个更大的块。因此,它更容易碎片化。但是,如果分配的块与后续释放的块大小始终相同,则碎片化不是问题。Heap_2 适合反复创建和删除任务的应用程序,但前提是分配给所创建的任务的堆栈大小不发生变化。

下图显示在创建和删除任务时从 heap_2 数组中分配和释放 RAM 的过程。

图中显示当创建、删除以及后续再次创建任务时,最适合算法的工作原理

• A显示已创建三个任务后的数组。大型可用块保持在数组的顶部。

• B显示已删除其中一个任务后的数组。这些区域有:

数组顶部的大型可用块保持原样。此外,目前有两个较小的可用块,它们之前分配给了已删除任务的TCB 和堆栈。

• C显示已创建另一个任务后的数组。创建任务导致两次调用 pvPortMalloc():一次是分配新的 TCB,一次是分配任务堆栈。使用 xTaskCreate() API 函数创建任务,如创建任务 (p. 23)中所述。在 xTaskCreate() 内部发生两次 pvPortMalloc() 调用。

每个 TCB 的大小完全相同,因此,最适合算法可确保之前分配给已删除任务的 TCB 的 RAM 块可重用于分配新任务的 TCB。

分配给新创建任务的堆栈大小与分配给以前被删除任务的堆栈大小完全相同,因此,最适合算法可确保之前分配给已删除任务的堆栈的 RAM 块可重用于分配新任务的堆栈。

数组顶部的较大的未分配块保持不变。Heap_2 是非确定性的,但它比 malloc() 和 free() 的大多数标准库实现都要快。

Heap_3

Heap_3.c 使用标准库函数 malloc() 和 free(),因此堆大小由链接器配置定义。configTOTAL_HEAP_SIZE 设置不起作用。

Heap_3 通过临时暂停 FreeRTOS 计划程序使 malloc() 和 free() 成为线程安全的。

Heap_4

与 heap_1 和 heap_2 一样,heap_4 将数组细分成较小的块。数组是静态声明的并由configTOTAL_HEAP_SIZE 确定大小,因此应用程序看起来会消耗大量 RAM,甚至在从数组中分配任何内存之前就是如此。

Heap_4 使用首个适合算法分配内存。与 heap_2 不同,它会将相邻的可用内存块组合(合并)成一个较大的块。这样可以最大程度地减小内存碎片化风险。

首个适合算法可确保 pvPortMalloc() 使用第一个大小足以容纳所要求的字节数的可用内存块。例如,考虑以下情形:

• 堆包含三个可用内存块。它们在数组中以下面的顺序显示:5 字节、200 字节和 100 字节。 • 调用 pvPortMalloc() 以请求 20 字节的 RAM。

适合所请求字节数的第一个可用 RAM 块是 200 字节块,因此,pvPortMalloc() 将 200 字节块拆分成一个 20字节块和一个 180 字节块,然后返回一个指向 20 字节块的指针。(上面是过于简化了,因为 heap_4 要存储堆区域中块大小的信息,因此两个拆分块的总和将小于 200 字节。) 新的 180 字节块保持可用于将来对pvPortMalloc() 的调用。

Heap_4 会将相邻的可用内存块组合(合并)成一个较大的块,同时最大限度地降低碎片化风险。Heap_4 适用于反复分配和释放不同大小的 RAM 块的应用程序。

下图显示从 heap_4 数组中分配和释放 RAM 的过程。它演示 heap_4 首个适合算法(带内存合并)在分配和释放内存时的工作方式。

A显示已创建三个任务后的数组。大型可用块保持在数组顶部。

B显示已删除其中一个任务后的数组。

C显示已创建一个 FreeRTOS 队列后的数组。

D显示在从应用程序代码中直接调用 pvPortMalloc()(而不是通过间接调用 FreeRTOS API 函数)后的数组。

E显示删除队列后的数组,此时会自动释放分配给已删除队列的内存。此时,用户分配的块的两侧都有可用内存。

F显示的也是已释放用户分配的内存之后的数组。用户分配的块所用的内存已与两侧的可用内存组合成一个更大的可用块。

Heap_4 是非确定性的,但比 malloc() 和 free() 的大多数标准库实现都要快。

Heap_5

heap_5 用来分配和释放内存的算法与 heap_4 的完全相同。与 heap_4 不同,heap_5 不限于从单个静态声明的数组分配内存。Heap_5 可以从多个单独的内存空间分配内存。当运行 FreeRTOS 的系统提供的 RAM在系统的内存映射中未作为单个邻接的(没有空间)块出现时,Heap_5 很有用。

Heap_5 是唯一一个必须在调用 pvPortMalloc() 之前显式初始化的内存分配方案。它使用 vPortDefineHeapRegions() API 函数进行初始化。当使用 heap_5 时,必须先调用vPortDefineHeapRegions(),然后才可创建任何内核对象(任务、队列、信号等)。

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

    关注

    40

    文章

    3372

    浏览量

    128132
  • 内存
    +关注

    关注

    8

    文章

    2737

    浏览量

    72613
  • FreeRTOS
    +关注

    关注

    11

    文章

    472

    浏览量

    61301
收藏 人收藏

    评论

    相关推荐

    请问ADuC7060能否移植uCOS II嵌入式操作系统

    我想问一下ADuC7060能否移植uCOS II嵌入式操作系统
    发表于 01-15 06:59

    FreeRTOS内存机制详解

    FreeRTOS是一种实时操作系统,它提供了多种内存分配方式,包括动态内存分配和静态内存分配。
    的头像 发表于 12-31 16:49 1021次阅读
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>内存</b>机制详解

    单片机操作系统有哪些

    FreeRTOSFreeRTOS是一种基于优先级的实时操作系统,广泛应用于嵌入式系统中。它具有小巧、高效、可移植的特点,并且支持多任
    的头像 发表于 12-08 16:18 1261次阅读

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

    FreeRTOS 作为一个嵌入式实时操作系统,其运行的环境一般资源有限
    的头像 发表于 11-13 11:28 400次阅读
    聊聊<b class='flag-5'>FreeRTOS</b><b class='flag-5'>内存</b><b class='flag-5'>管理</b>方案及相关的优化措施

    一种嵌入式操作系统—VxWorks

    电子发烧友网站提供《一种嵌入式操作系统—VxWorks.pdf》资料免费下载
    发表于 10-27 10:59 0次下载
    一种<b class='flag-5'>嵌入式</b><b class='flag-5'>操作系统</b>—VxWorks

    如何在AT32F系列MCU上使用FreeRTOS

    本指导手册描述了如何在AT32F系列MCU上使用FreeRTOSFreeRTOS是一款开源的嵌入式实时操作系统,目前在各种嵌入式应用中应用
    发表于 10-24 07:46

    什么是嵌入式Linux?

    Linux到底是什么呢? 嵌入式linux 是将日益流行的Linux操作系统进行裁剪修改,使之能在嵌入式计算机系统上运行的一种操作系统。简单来说,是除了电脑之外可以运行程序的设备,将C
    发表于 10-11 13:47

    FreeRTOS内存管理实现

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

    STM32嵌入式操作系统介绍

    的分配、任务调度,控制、协调并发活动。• 目前在嵌入式领域广泛使用的操作系统有:嵌入式实时操作系统FreeRTOS、µC/OS-II、RTh
    发表于 09-28 06:59

    嵌入式操作系统IJLC/OS一Ⅱ在通信电源监控中应用

    的单片机开发相比.使用实时嵌入式操作系统。利用其多任务管理,任务间同步与通信等特点,可以在一定程度上提高系统的可靠性和实时性。嵌入式实时
    发表于 09-20 06:46

    FreeRTOS实时操作系统简述

    FreeRTOS是一个为嵌入式系统设计的开源实时操作系统。它提供了一个多任务内核和一系列组件功能,适合在资源受限的设备上管理实时任务和应用程
    发表于 09-19 12:37 588次阅读
    <b class='flag-5'>FreeRTOS</b>实时<b class='flag-5'>操作系统</b>简述

    STM32嵌入式操作系统介绍

    的分配、任务调度,控制、协调并发活动。• 目前在嵌入式领域广泛使用的操作系统有:嵌入式实时操作系统FreeRTOS、µC/OS-II、RTh
    发表于 09-11 07:24

    汽车操作系统和中间件的关系

    如果嵌入式操作系统(比如FreeRTOS, uCOS或Autosar CP的内核)的开发难度和工作量是1, 那么大型操作系统的开发难度和工作量就是99, 从体量就能看出,一个完整的
    发表于 07-21 11:16 397次阅读
    汽车<b class='flag-5'>操作系统</b>和中间件的关系

    FreeRTOS:一个迷你的实时操作系统内核

    ** 1、FreeRTOS** FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间
    的头像 发表于 06-29 17:15 486次阅读
    <b class='flag-5'>FreeRTOS</b>:一个迷你的实时<b class='flag-5'>操作系统</b>内核

    什么是ARM-Linux嵌入式操作系统移植呢?

    什么是ARM-Linux嵌入式操作系统移植呢?
    发表于 05-16 10:22