FreeRTOS的内存管理(选择heap4.c文件的理由)

       

目录

1. 了解FreeRTOS内存管理

2. 了解内存碎片

3.了解各个heap.c的内存分配方法

1.heap1.c

2.heap2.c

3.heap3.c

4.heap4.c

5.heap5.c

总结:


      内存管理是一个系统基本组成部分,FreeRTOS 中大量使用到了内存管理,比如创建任务、信号量、队列等会自动从堆中申请内存,用户应用层代码也可以 FreeRTOS 提供的内存管理函数来申请和释放内存,他们是heap1~5.c,下面就粗略的讲解一下他们的特性。

1. 了解FreeRTOS内存管理

      FreeRTOS创建任务、队列、信号量等的时候有两种方法,一种是动态的申请所需的RAM 一种是由用户自行定义所需的 RAM,这种方法也叫静态方法,使用静态方法的函数一般以 Static”结尾,比如任务创建函数 xTaskCreateStatic(),使用此函数创建任务的时候需要由用户定义任务堆栈,本章我们不讨论这种静态方法。

     使用动态内存管理的时候 FreeRTOS  内核在创建任务、队列、信号量的时候会动态的申请RAM。标准 C 库中的 malloc() free()也可以实现动态内存管理,但是如下原因限制了其使用:

● 在小型的嵌入式系统中效率不高。

● 会占用很多的代码空间。

● 它们不是线程安全的。

● 具有不确定性,每次执行的时间不同。

● 会导致内存碎片。

● 使链接器的配置变得复杂。

       不同的嵌入式系统对于内存分配和时间要求不同,因此一个内存分配算法可以作为系统的可选选项。FreeRTOS 将内存分配作为移植层的一部分,这样 FreeRTOS 使用者就可以使用自己的合适的内存分配方法。

      当内核需要 RAM 的时候可以使用 pvPortMalloc()来替代 malloc()申请内存,不使用内存的时候可以使用 vPortFree()函数来替代 free()函数释放内存。函数 pvPortMalloc()vPortFree()与函  malloc() free()的函数原型类似。

      FreeRTOS 提供了 5 种内存分配方法,FreeRTOS 使用者可以其中的某一个方法,或者自己的内存分配方法。这 5 种方法是 5 个文件,分别为:heap_ 1.c heap_2.c heap_3.c heap_4.c  heap_5.c。这 5 个文件再 FreeRTOS 源码中,路径:FreeRTOS->Source->portable->MemMang  后面会详细讲解这 5 种方法有何区别。

2. 了解内存碎片

在看 FreeRTOS  的内存分配方法之前我们先来看一下什么叫做内存碎片,看名字就知道是 小块的、碎片化的内存。那么内存碎片是怎么来的呢?内存碎片是伴随着内存申请和释放而来 的,如图所示。

(1)、此时内存堆还没有经过任何操作,为全新的。

(2)、此时经过第一次内存分配,一共分出去了 4 块内存块,大小分别为 80B80B 10B  100B

(3)、有些应用使用完内存,进行了释放,从左往右第一个 80B 和后面的 10B 这两个内存块 就是释放的内存。如果此时有个应用需要 50B 的内存,那么它可以从两个地方来获取到,一个 是最前面的还没被分配过的剩余内存块,另一个就是刚刚释放出来的 80B 的内存块。但是很明 显,刚刚释放出来的这个 10B 的内存块就没法用了,除非此时有另外一个应用所需要的内存小  10B

(4)、经过很多次的申请和释放以后,内存块被不断的分割、最终导致大量很小的内存块! 也就是图中 80B  50B 这两个内存块之间的小内存块,这些内存块由于太小导致大多数应用无 法使用,这些没法使用的内存块就沦为了内存碎片!

内存碎片是内存管理算法重点解决的一个问题,否则的话会导致实际可用的内存越来越少, 最终应用程序因为分配不到合适的内存而奔溃!FreeRTOS   heap_4.c 就给我们提供了一个解 决内存碎片的方法,那就是将内存碎片进行合并组成一个新的可用的大内存块。

3.了解各个heap.c的内存分配方法

1.heap1.c

heap_ 1 实现起来就是当需要 RAM  的时候就从一个大数组(内存堆)中分一小块出来,大数(内存堆)的容量为 configTOTAL_HEAP_SIZE,上面已经说了。使用函数xPortGetFreeHeapSize() 可以获取内存堆中剩余内存大小。

注意!!!heap1.c没有内存释放函数!

heap_1 的特性如下:

1、适用于那些一旦创建好任务、信号量和队列就再也不会删除的应用,实际上大多数的 FreeRTOS 应用都是这样的。

2、具有可确定性(执行所花费的时间大多数都是一样的),而且不会导致内存碎片。

3、代码实现和内存分配过程都非常简单,内存是从一个静态数组中分配到的,也就是适合 于那些不需要动态内存分配的应用。

2.heap2.c

heap_2 提供了一个更好的分配算法,不像heap_ 1 那样,heap_2供了内存释放函数heap_2 不会把释放的内存块合并成一个大块,这样有一个缺点,随着你不断的申请内存,内存堆就会被分为很多个大小不一的内存(),也就是会导致内存碎片!heap_4  提供了空闲内存块合并的功能。

heap_2 的特性如下:

1、可以使用在那些可能会重复的删除任务、队列、信号量等的应用中,要注意有内存碎片产生!

2、如果分配和释放的内存 n 大小是随机的,那么就要慎重使用了,比如下面的示例:

      ●  如果一个应用动态的创建和删除任务,而且任务需要分配的堆栈大小都是一样的,那么heap_2 就非常合适。如果任务所需的堆栈大小每次都是不同,那么 heap_2  不适合了,因为这样会导致内存碎片产生,最终导致任务分配不到合适的堆栈!不heap_4 就很适合这种场景了。

      ●  如果一个应用中所使用的队列存储区域每次都不同,那么 heap_2 就不适合了,和上面一样,此时可以使用 heap_4

     ●  应用需要调用 pvPortMalloc() vPortFree()来申请和释放内存,而不是通过其他FreeRTOS的其他 API 函数来间接的调用,这种情况下 heap_2 不适合。

3、如果应用中的任务、队列、信号量和互斥信号量具有不可预料性(如所需的内存大小不能确定,每次所需的内存都不相同,或者说大多数情况下所需的内存都是不同的)的话可能会导致内存碎片。虽然这是小概率事件,但是还是要引起我们的注意!

4、具有不可确定性,但是也远比标准 C 中的 malloc() free()效率高!

      heap_2 基本上可以适用于大多数的需要动态分配内存的工程中,而 heap_4 更是具有将内存 碎片合并成一个大的空闲内存块(就是内存碎片回收)功能。

3.heap3.c

这个分配方法是对标准 C 中的函数 malloc() free()的简单封装,FreeRTOS 对这两个函数做了线程保护。

heap_3 的特性如下:

1、需要编译器提供一个内存堆,编译器库要提供 malloc() free()函数。比如使用 STM32 的话可以通过修改启动文件中的 Heap_Size 来修改内存堆的大小,如图所示

2、具有不确定性

3 、可能会增加代码量。

注意,在 heap_3  configTOTAL_HEAP_SIZE 是没用的!

4.heap4.c

heap_4提供了一个最优的匹配算法,不heap_2heap_4会将内存碎片合并成一个大的可用内存块,它提供了内存块合并算法。内存堆为ucHeap[],大小同样为 configTOTAL_HEAP_SIZE 可以通过函数 xPortGetFreeHeapSize()来获取剩余的内存大小。

heap_4 特性如下:

1 、可以用在那些需要重复创建和删除任务、队列、信号量和互斥信号量等的应用中。

2、不会像 heap_2 那样产生严重的内存碎片,即使分配的内存大小是随机的。

3、具有不确定性,但是远比 C 标准库中的 malloc() free()效率高。

heap_4 非常适合于那些需要直接调用函数 pvPortMalloc()vPortFree()来申请和释放内存的应用,注意,我们移植 FreeRTOS 的时候就选择的 heap_4

heap_4 也使用链表结构来管理空闲内存块,链表结构体与 heap_2 一样。heap_4 也定义了两个局部静态变量 xStart  pxEnd 来表示链表头和尾,其中 pxEnd 是指向 BlockLink_t 的指针。

5.heap5.c

       heap_5 使用了和 heap_4 相同的合并算法,内存管理实现起来基本相同,但是heap_5允许内存堆跨越多个不连续的内存段。比如STM32 的内部 RAM 可以作为内存堆,但是STM32RAM比较小,遇到那些需要大容量 RAM 的应用就不行了,如音视频处理。不过 STM32  以外接 SRAM  甚至大容量的 SDRAM,如果使用 heap_4  的话你就只能在内部 RAM  和外部 SRAM SDRAM 之间二选一了,使用 heap_5 的话就不存在这个问题,两个都可以一起作为内存堆来用。

       如果使用 heap_5 的话,在调用 API 函数之前需要先调用函数vPortDefineHeapRegions()对内存堆做初始化处理,在vPortDefineHeapRegions()未执行完之前禁止调用任何可能会调用pvPortMalloc() API 函数!比如创建任务、信号量、队列等函数。函数vPortDefineHeapRegions() 只有一个参数,参数是一个 HeapRegion_t 类型的数组,HeapRegion 为一个结构体,此结构体在 portable.h 中有定义,这里就不例出例子了。    

      注意,数组中成员顺序按照地址从低到高的顺序排列,而且最后一个成员必须使用NULLheap_5 允许内存堆不连续,说白了就是允许有多个内存堆。在 heap_2  heap_4 中只有一个内 存堆,初始化的时候只也只需要处理一个内存堆。 heap_5 有多个内存堆,这些内存堆会被连接 在一起,和空闲内存块链表类似,这个处理过程由函数 vPortDefineHeapRegions()完成。

      使用heap_5  的时候在一开始就应该先调用函数 vPortDefineHeapRegions()完成内存堆的初始化!然后才能创建任务、信号量这些东西。

//使用heap_5 的时候在开启任务调度器、创建任务、创建信号量之前一定要先

//调用函数 vPortDefineHeapRegions()初始化内存堆!

vPortDefineHeapRegions((const HeapRegion_t *)xHeapRegions);

      heap_5 的内存申请和释放函数和 heap_4 基本一样,这里就不详细讲解了,大家可以对照着前面 heap_4 的相关内容来自行分析。

     至此,FreeRTOS 官方提供的 5 种内存分配方法已经讲完了heap_1 最简单,但是只能申请 内存,不能释放。heap_2 提供了内存释放函数,用户代码也可以直接调用函数 pvPortMalloc() vPortFree()来申请和释放内存,但是 heap_2 会导致内存碎片的产生!heap_3 对标准 C 库中的 函数 malloc() free()的简单封装,并且提供了线程保护。heap_4 相对与 heap_2 提供了内存合 并功能,可以降低内存碎片的产生,我们移植 FreeRTOS 时候就选择了 heap_4 heap_5 基本 上和 heap_4 一样,只是 heap_5 支持内存堆使用不连续的内存块。

总结:

       heap.c虽然在使用的时候可以傻瓜式的直接无脑使用heap.4,但从heap1到heap5,我们可以看到FreeRTOS对于更加有秀的内存分配方法所作出的努力,从heap1只能固定格式分配、没提供内存删除函数、到heap4提供内存合并算法,就像牙牙学语的婴儿逐步成长成为了巨人,越来越强大。

     到这里,FreeRTOS的学习也告一段落,FreeRTOS中的队列、信号量、列表、软件定时器、配置API函数、事件组、任务通知、延迟函数、空闲函数、优先级希望大家有学会并有所收获,下面就是做实际的项目边学边练,希望大家能够将FreeRTOS学到手!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/945982.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[服务器][教程]Ubuntu24.04 Server开机自动挂载硬盘教程

1. 查看硬盘ID ls -l /dev/disk/by-uuid可以看到对应的UUID所对应的分区 2. 创建挂载文件夹 创建好文件夹即可 3. 修改配置文件 sudo vim /etc/fstab把对应的UUID和创建的挂载目录对应即可 其中# Personal mount points下面的是自己新添加的 :分区定位&#xff…

Python用K-Means均值聚类、LRFMC模型对航空公司客户数据价值可视化分析指标应用|数据分享...

全文链接:https://tecdat.cn/?p38708 分析师:Yuling Fang 信息时代的来临使得企业营销焦点从产品中心转向客户中心,客户关系管理成为企业的核心问题(点击文末“阅读原文”获取完整代码数据)。 客户关系管理的关键是客…

HTML——46.制作课程表

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>课程表</title></head><body><h3>课程表</h3><table border"1" cellspacing"0"><tr><th colspan"…

强化学习(1)

Reinforcement Learning Goal-directed learing from ineraction with the environment. 1. Basic Element 基本元素 1.1 Agent 玩家 1.2 Environment 1.3 Goal 2. Main Element 主要元素 2.1 State 2.2 Action 状态与行为往复 2.3 Reward 目标&#xff1a;最大化总…

《代码随想录》Day21打卡!

写在前面&#xff1a;祝大家新年快乐&#xff01;&#xff01;&#xff01;2025年快乐&#xff0c;2024年拜拜~~~ 《代码随想录》二叉树&#xff1a;修剪二叉搜索树 本题的完整题目如下&#xff1a; 本题的完整思路如下&#xff1a; 1.本题使用递归进行求解&#xff0c;所以分…

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针-CSDN博客 类型含义使用场景示例nil表示一个指向 Objective - C 对象的空指针。在 Objective - C 和 Swift&#xff08;与 Objective - C 交互时&#xff09;中用于表示对象不存在。当一个对象变量没有指向任何有效的对象实例…

CPT203 Software Engineering 软件工程 Pt.6 软件管理(中英双语)

文章目录 10. Project Management&#xff08;项目管理&#xff09;10.1 Project Management Overview10.1.1 Project Management Importance&#xff08;项目管理的重要性&#xff09;10.1.2 Criteria for Project Management&#xff08;项目管理的准则&#xff09;10.1.3 Ch…

Java [后端] 开发日常记录(1)

目录 1、常用的注解 2、对字符串的处理 3、对JSON串的处理 -- The End -- 详细如下&#xff1a; 1、常用的注解 若返回的字段中有NUll&#xff0c;则不返回 JsonInclude(value JsonInclude.Include.NON_NULL) //在实体类中添加这个注解 JsonInclude(JsonInclude.Include.NON…

流计算需要框架吗?SPL 可能是更好的选择

流数据源通常是动态、无界的&#xff0c;看起来与静态、有限的批数据源区别较大&#xff0c;传统的数据库技术在架构上难以直接处理流数据源&#xff0c;只能让位于后来者。heron\samza\storm\spark\flink等计算框架最先完成突破&#xff0c;在流计算技术中占得先发优势。这些框…

设计模式の状态策略责任链模式

文章目录 前言一、状态模式二、策略模式三、责任链模式 前言 本篇是关于设计模式中的状态模式、策略模式、以及责任链模式的学习笔记。 一、状态模式 状态模式是一种行为设计模式&#xff0c;核心思想在于&#xff0c;使某个对象在其内部状态改变时&#xff0c;改变该对象的行为…

鸿蒙UI开发——使用WidthTheme实现局部深浅色

1、场景描述 在实际的应用开发中&#xff0c;我们可能需要在界面中局部应用深色或者浅色的界面样式&#xff0c;与全局的深色、亮色同时生效。场景例如&#xff1a;深/亮色预览。此时&#xff0c;我们可以使用WithTheme能力来达到我们的效果。 2、WithTheme WithTheme组件可…

20241231取消掉夸克浏览器为默认浏览器

20241231取消掉夸克浏览器为默认浏览器 2024/12/31 17:59 因为有些资源必须用夸克网盘下载&#xff01;^_ 地区特色问题。对于百度网盘&#xff0c;如果你分享BBC的纪录片合集&#xff0c;马上给你无效掉&#xff01;^_ 但是夸克有一点夜郎自大了&#xff0c;把客户的默认浏览器…

详细教程:SQL2008数据库备份与还原全流程!

数据的安全性至关重要&#xff0c;无论是操作系统、重要文件、磁盘存储&#xff0c;还是企业数据库&#xff0c;备份都是保障其安全和完整性的关键手段。拥有备份意味着即使发生误删、系统崩溃或病毒攻击等问题&#xff0c;也能迅速通过恢复功能解决&#xff0c;避免数据丢失带…

一、Hadoop概述

文章目录 一、Hadoop是什么二、Hadoop发展历史三、Hadoop三大发行版本1. Apache Hadoop2. Cloudera Hadoop3. Hortonworks Hadoop 四、Hadoop优势1. 高可靠性2. 高扩展性3. 高效性4. 高容错性 五、Hadoop 组成1. Hadoop1.x、2.x、3.x区别2. HDFS 架构概述3. YARN 架构概述4. Ma…

docker-开源nocodb,使用已有数据库

使用已有数据库 创建本地数据库 数据库&#xff1a;nocodb 用户&#xff1a;nocodb 密码&#xff1a;xxxxxx修改docker-compose.yml 默认网关的 IP 地址是 172.17.0.1&#xff08;适用于 bridge 网络模式&#xff09;version: "2.1" services:nocodb:environment:…

BetterBench的2024年终总结

回忆录 去年的年末定的2024目标是阅读300篇文献&#xff0c;发表一篇小论文&#xff0c;阅读20本的目标&#xff0c;都没有如期完成。只读了130篇论文&#xff0c;小论文还只写了初稿&#xff0c;还没有投出去&#xff0c;只读了6本书&#xff0c;上半年很浮躁&#xff0c;都没…

编辑音频的基本属性

导入音频 “文件-导入-选择音频”拖到音频轨道创建序列。选择音频&#xff0c;在效果空间可以看到音频的基本属性。 音量的设置 “效果工作区-效果控件-音量”在这里可以控制所有引导的混合音量 静音 静止所有声音 音频仪表 一般位于时间轴的后面&#xff0c;找不到可以…

SQL 基础教程 - SQL SELECT 语句

SQL SELECT DISTINCT 语句 SELECT DISTINCT 语句用于返回唯一不同的值。 在表中&#xff0c;一个列可能会包含多个重复值&#xff0c;有时您也许希望仅仅列出不同&#xff08;distinct&#xff09;的值。 DISTINCT 关键词用于返回唯一不同的值。 SQL SELECT DISTINCT 语法 …

Oracle 回归分析函数使用

Oracle 回归分析函数使用 文章目录 Oracle 回归分析函数使用什么是 回归分析函数回归分析函数示例1. 分析 SAL 和 COMM 之间的回归关系2. 按部门分析 SAL 和 COMM 的关系3. 根据 SAL 预测 COMM4. 分析员工薪资与工作年限的关5. 按部门分析工作年限与薪资的关系6. 计算 REGR_AVG…

idea项目导入gitee 码云

1、安装gitee插件 IDEA 码云插件已由 gitosc 更名为 gitee。 1 在码云平台帮助文档http://git.mydoc.io/?t153739上介绍的很清楚&#xff0c;推荐前两种方法&#xff0c; 搜索码云插件的时候记得名字是gitee&#xff0c;gitosc已经搜不到了。 2、使用码云托管项目 如果之…