【FreeRTOS】内存管理

目录

  • 1 为什么要自己实现内存管理
  • 2 FreeRTOS的5中内存管理方法
    • 2.1 Heap_1
    • 2.2 Heap_2
    • 2.3 Heap_3
    • 2.4 Heap_4
  • 2.5 Heap_5
  • 3 Heap相关的函数
    • 3.1 pvPortMalloc/vPortFree
    • 3.2 xPortGetFreeHeapSize
  • 3.3 xPortGetMinimumEverFreeHeapSize
    • 3.4 malloc失败的钩子函数

参考《FreeRTOS入门与工程实践(基于DshanMCU-103)》

1 为什么要自己实现内存管理

后续的章节涉及这些内核对象:task、queue、semaphores和event group等。为了让FreeRTOS更容易使用,这些内核对象一般都是动态分配:用到时分配,不使用时释放。使用内存的动态管理功能,简化了程序设计:不再需要小心翼翼地提前规划各类对象,简化API函数的涉及,甚至可以减少内存的使用。

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

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

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

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

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

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

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

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

Alt

2 FreeRTOS的5中内存管理方法

FreeRTOS中内存管理的接口函数为: pvPortMalloc 、 vPortFree,对应于 C库的 malloc、
free。

  • 文件在FreeRTOS/Source/portable/MemMang下,它也是放在portable目录下,表示你可以提供自己的函数。
    源码中默认提供了5个文件,对应内存管理的5种方法

  • 文件在 Middlewares\Third_Party\FreeRTOS\Source\portable\MemMang 下,它也是放在“portable”目录下,表示你可以提供自己的函数。

源码中默认提供了5个文件,对应内存管理的5种方法。

参考文章:http://t.csdnimg.cn/wCCzx

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

2.1 Heap_1

Heap_1 它只实现了pvPortMalloc,没有实现vPortFree。 只分配 不回收,一点都不浪费空间
如果你的程序不需要删除内核对象,那么可以使用heap_1:

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

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

/* Allocate the memory for the heap. */
##if ( configAPPLICATION_ALLOCATED_HEAP == 1 )

/* The application writer has already defined the array used for the RTOS
* heap -  probably so it can be placed in a special segment or address. */
    extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
##else
    static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
##endif /* configAPPLICATION_ALLOCATED_HEAP */

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

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

  • A:创建任务之前整个数组都是空闲的

  • B:创建第 1 个任务之后,蓝色区域被分配出去了

  • C:创建 3 个任务之后的数组使用情况

在这里插入图片描述

在这里插入图片描述

2.2 Heap_2

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、栈大小一致,所以刚好分配到原来的内存

在这里插入图片描述

heap_2 既分配又释放
在这里插入图片描述
假设buf1是申请了100个字节,有头部,buf2是申请了50个字节,也有头部~

假设释放buf1和buf2,这两块内存是紧密相连的,但是它不能合并到一起,buf1仍然最大能分配100字节,buf2仍然是最大能分配50字节,假设buf3把最后面的内存都用完了,现在再想分配120字节的空间,用heap2这种方法就没有办法分配了!原因&缺点:它没有把空闲的紧密相连的空间合并在一起,所以有严重的碎片问题。

2.3 Heap_3

heap_3一般不用,调用标准库

  • Heap_3 使用标准 C 库里的 malloc、 free 函数,所以堆大小由链接器的配置决定,配置项 configTOTAL_HEAP_SIZE 不再起作用。
  • C库里的malloc、 free函数并非线程安全的, Heap_3中先暂停FreeRTOS的调度器,再去调用这些函数,使用这种方法实现了线程安全.

2.4 Heap_4

跟 Heap_1、 Heap_2 一样, Heap_4 也是使用大数组来分配内存
Heap_4使用首次适应算法(first fit)来分配内存。它还会把相邻的空闲内存合并为一个更大的空闲内存,这有助于较少内存的碎片问题

首次适应算法:

  • 假设堆中有 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。

总结:

heap_4 既分配又释放, heap_4在heap_2的基础上做了一些改进,heap_4合并相邻的空闲内存

针对heap_2的缺点,如果我们再想分配120字节的空间,那就是可以的了,因为buf1的100字节和buf2的50字节合并起来了!

heap_5 用来支持分隔的内存

两块红色的就是分隔的内存,可以用heap_5来管理
在这里插入图片描述
空闲链表头,指向第一个堆,再指向第二个堆,我们需要告诉链表头,有多少个离散的堆

一般的都是用heap_4

2.5 Heap_5

Heap_5 分配内存、释放内存的算法跟 Heap_4 是一样的。
相比于Heap_4, Heap_5并不局限于管理一个大数组:它可以管理多块、分隔开的内存。
在嵌入式系统中,内存的地址可能并不连续,这种场景下可以使用Heap_5。
既然内存时分隔开的,那么就需要进行初始化:确定这些内存块在哪、多大:

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

  • 使用 vPortDefineHeapRegions 来指定这些信息

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

typedef struct HeapRegion
{
    uint8_t * pucStartAddress; // 起始地址
    size_t xSizeInBytes;       // 大小
} HeapRegion_t;

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

HeapRegion_t xHeapRegions[] =
{
  { ( uint8_t * ) 0x80000000UL, 0x10000 }, // 起始地址0x80000000,大小0x10000
  { ( uint8_t * ) 0x90000000UL, 0xa0000 }, // 起始地址0x90000000,大小0xa0000
  { NULL, 0 } // 表示数组结束
 };

vPortDefineHeapRegions函数原型如下:

void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );

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

3 Heap相关的函数

这里定义了一个全局数组
在这里插入图片描述

什么时候被用到,初始化堆的时候就用到了
在这里插入图片描述
这个函数在哪里被调用呢
在我们第一次调用malloc函数的时候,就自动初始化了,不需要手动调用HeapInit
在这里插入图片描述
用完之后,再调用pvPortFree就可以了
在这里插入图片描述

3.1 pvPortMalloc/vPortFree

函数原型:

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

作用:分配内存、释放内存。

如果分配内存不成功,则返回值为NULL。

3.2 xPortGetFreeHeapSize

函数原型:

size_t xPortGetFreeHeapSize( void );

当前还有多少空闲内存,这函数可以用来优化内存的使用情况。比如当所有内核对象都分配好后,执行此函数返回2000,那么configTOTAL_HEAP_SIZE就可减小2000。

注意:在heap_3中无法使用。

3.3 xPortGetMinimumEverFreeHeapSize

函数原型:

size_t xPortGetMinimumEverFreeHeapSize( void )

返回:程序运行过程中,空闲内存容量的最小值。

注意:只有heap_4、heap_5支持此函数。

使用:让程序跑一段时间再调用这个函数,如果剩余容量只有两位数了,就很危险了,就需要把3072调的大一些才可以!

  • 这个函数是测试用的

3.4 malloc失败的钩子函数

在pvPortMalloc函数内部:

函数原型:

void * pvPortMalloc( size_t xWantedSize )vPortDefineHeapRegions
{
    ......
    #if ( configUSE_MALLOC_FAILED_HOOK == 1 )
        {
            if( pvReturn == NULL )
            {
                extern void vApplicationMallocFailedHook( void );
                vApplicationMallocFailedHook();
            }
        }
    #endif
    
    return pvReturn;        
}

所以,如果想使用这个钩子函数:

  • 在FreeRTOSConfig.h中,把configUSE_MALLOC_FAILED_HOOK定义为1

  • 提供vApplicationMallocFailedHook函数

  • pvPortMalloc失败时,才会调用此函数

在这里插入图片描述

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

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

相关文章

Python私教张大鹏 Vue3整合AntDesignVue之DatePicker 日期选择框

案例&#xff1a;选择日期 <script setup> import {ref} from "vue";const date ref(null) </script> <template><div class"p-8 bg-indigo-50 text-center"><a-date-picker v-model:value"date"/><a-divide…

原子阿波罗STM32F429程序的控制器改为STM32F407驱动LCD屏

原子大神的阿波罗开发板使用STM32F429IGT6控制器&#xff0c;编程风格也与探索者F407系列有了很大的不同&#xff0c;使用BSP功能模块编程了&#xff0c;也有点类似于安富莱的编程风格了。这种模块式程序风格的优点是更加方便移植&#xff0c;更方便泡系统。 但无奈手里只有F40…

模拟笔试 - 卡码网周赛第二十一期(23年美团笔试真题)

第一题&#xff1a;小美的排列询问 解题思路: 简单题&#xff0c;一次遍历数组&#xff0c;判断 是否有和x、y相等并且相连 即可。 可优化逻辑&#xff1a;因为x和y是后输入的&#xff0c;必须存储整个数组&#xff0c;但是上面说了 **排列是指一个长度为n的数组&#xff0…

搭建一个好玩的 RSS 订阅网站记录

全文相关链接 Github仓库创建链接Railway官网Supabase官网f-droid上的co.appreactor.news应用下载链接Railway账户使用量估算链接 全文相关代码 原文地址: https://blog.taoshuge.eu.org/p/270/ Dockerfile FROM docker.io/miniflux/miniflux:2.1.3环境变量 DATABASE_URL…

UniApp或微信小程序中scroll-view组件使用show-scrollbar在真机Android或IOS中隐藏不了滚动条的解决办法

show-scrollbar 属性 不论是使用 变量 还是直接使用 布尔值或者直接使用 css 都是在 ios、Android 上是都没有效果。。 真机中还是出现滚动条 解决办法 添加下面CSS ::-webkit-scrollbar {display: none;width: 0 !important;height: 0 !important;-webkit-appearance: no…

Charles代理https接口到本地

一、操作手册 1、安装工具 1.1、安装代理软件Charles 软件下载地址&#xff1a;Download a Free Trial of Charles • Charles Web Debugging Proxy 1.2、安装https代理插件&#xff1a;&#xff08;有问题自行百度解决&#xff09; 2、配置策略 以下以https接口为例&…

mysql索引B+树可视化演示地址

https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

RISE - Ultimate Project Manager CRM 3.6.1 中文版安装指南

RISE 是一个多用途项目管理系统&#xff0c;有助于任何类型的企业管理他们的工作。它可以节省您管理客户、项目、销售和团队成员的日常时间。可提高客户满意度和工作绩效。 安装系统 登录宝塔&#xff0c;添加站点 输入域名和数据库信息&#xff0c;PHP版本至少是8.1 添加完成…

下载kibana-7.10.2教程

1、官网下载地址&#xff1a; Download Kibana Free | Get Started Now | Elastic 2、进入 Kibana下载界面&#xff0c;点击 View past releases 查看过去的版本 3、选择版本 Elasticsearch 7.10.2&#xff0c;点击 Download 4、点击 LINUX 64-BIT&#xff0c;进行下载 5、下…

docker-compose Install it-tools

IT-Tools前言 IT-Tools是一款开源的个人工具箱,专为IT从业人员打造,支持Docker私有化部署,包含众多实用的IT工具。其功能丰富多样,涵盖二维码生成、数据格式转换、MAC地址生成等,可满足用户多样化的需求。 前提要求 安装 docker docker-compose 参考创建一键部署it-tool…

码农学点儿经济学-博傻理论

博傻理论 一位石油大佬去天堂开会&#xff0c;他兴冲冲地跑进会议室&#xff0c;却发现座无虚席&#xff0c;早已经没了他的座位。于是他灵机一动&#xff0c;大喊一声&#xff1a;大家注意啦&#xff01;听说&#xff0c;有人在地狱发现了石油&#xff01;此言一出&#xff0c…

电脑桌面上用来记事的便签软件

便签软件已成为我们日常生活中不可或缺的记录工具。想象一下&#xff0c;在繁忙的工作中&#xff0c;你突然需要记下一个重要事项或临时想法&#xff0c;这时&#xff0c;一个便捷、高效的便签软件就显得尤为重要。它能帮助我们迅速捕捉信息&#xff0c;轻松管理琐碎事务&#…

22 CRT工具安装流程

22 CRT工具安装流程 SecureCRT 9.5 说明书 SecureCRT 9.5是一款由VanDyke Software开发的终端仿真程序。它为Windows、Mac和Linux操作系统提供了强大的SSH&#xff08;Secure Shell&#xff09;客户端功能。SecureCRT 9.5提供了对Telnet、RLogin、Serial和X.509等协议的支持&…

C# WPF 读写CAN数据

C# WPF 读写CAN数据 CAN 分析仪 分析仪资料下载 官方地址&#xff1a;https://www.zhcxgd.com/1.html CSDN&#xff1a; 项目配置 复制Dll库文件 文件在上面的资料里面 设置不安全代码 CAN C#工具类 CAN_Tool.cs using Microsoft.VisualBasic; using System; using Sys…

el-select filterable模糊搜索在iOS手机上无法弹出软键盘,解决方案

前提&#xff1a; el-select filterable模糊搜索在iOS手机上无法弹出软键盘&#xff0c;在手机上使用时&#xff0c;iOS手机&#xff0c;该组件无法唤起软键盘&#xff0c;导致没法进行模糊搜素。 于是。开始去找原因&#xff0c;发现主要是因为 组件中&#xff0c;input上有一…

Day 21:2807. 在链表中插入最大公约数

Leetcode 2807. 在链表中插入最大公约数 给你一个链表的头 head &#xff0c;每个结点包含一个整数值。 在相邻结点之间&#xff0c;请你插入一个新的结点&#xff0c;结点值为这两个相邻结点值的 最大公约数 。 请你返回插入之后的链表。 两个数的 最大公约数 是可以被两个数字…

【MySQL】(基础篇十) —— 汇总数据(聚集函数)

汇总数据 本文介绍什么是SQL的聚集函数以及如何利用它们汇总表的数据。 聚集函数 我们经常需要汇总数据而不用把它们实际检索出来&#xff0c;为此MySQL提供了专门的函数。使用这些函数&#xff0c;MySQL查询可用于检索数据&#xff0c;以便分析和报表生成。这种类型的检索例…

css设置滚动条样式;滚动条设置透明

滚动条透明代码 .resizable-div {resize: both;/* 允许水平和垂直调整大小 */overflow: auto;/* 确保内容超出边界时出现滚动条 */ } /* 滚动条整体样式 */ .resizable-div::-webkit-scrollbar {width: 4px; /* 竖直滚动条宽度 */height: 4px; /* 水平滚动条高度 */ }/* 滚动条…

CCF 矩阵重塑

第一题&#xff1a;矩阵重塑&#xff08;一&#xff09; 本题有两种思路 第一种 &#xff08;不确定是否正确 但是100分&#xff09; #include<iostream> using namespace std; int main(){int n,m,p,q,i,j;cin>>n>>m>>p>>q;int a[n][m];for(i…

共模信号与差模信号

差模信号又称串模信号&#xff0c;指的是两根线之间的信号差值&#xff1b;而共模信号又称对地信号&#xff0c;指的是两根线分别对地的信号。 差模信号&#xff1a;大小相等&#xff0c;方向相反的信号。共模信号&#xff1a;大小相等&#xff0c;方向相同的信号。 对于两输…