FreeROTS学习 内存管理

内存管理是一个系统基本组成部分,FreeRTOS 中大量使用到了内存管理,比如创建任务、信号量、队列等会自动从堆中申请内存,用户应用层代码也可以 FreeRTOS 提供的内存管理函数来申请和释放内存

FreeRTOS 内存管理简介

FreeRTOS 创建任务、队列、信号量等的时候有两种方法,一种是动态的申请所需的 RAM。一种是由用户自行定义所需的 RAM,这种方法也叫静态方法

不同的嵌入式系统对于内存分配和时间要求不同,因此一个内存分配算法可以作为系统的可选选项。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 源码中

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

学习内存分配方法之前我们先来看一下什么叫做内存碎片
在这里插入图片描述
有些应用使用完内存,进行了释放,从左往右第一个 80B 和后面的 10B 这两个内存块就是释放的内存。如果此时有个应用需要 50B 的内存,那么它可以从两个地方来获取到,一个是最前面的还没被分配过的剩余内存块,另一个就是刚刚释放出来的 80B 的内存块。但是很明显,刚刚释放出来的这个 10B 的内存块就没法用了,除非此时有另外一个应用所需要的内存小于 10B;

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

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

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

内存管理方法简介优点缺点
heap_1.c简单静态分配方式,提供单一的内存堆,分配后内存块不会被释放,内存块大小在编译时确定。实现简单,占用资源少,无内存碎片问题,对于资源分配固定的简单系统可靠性高。缺乏灵活性,不能动态释放内存,不适用于需要频繁分配和释放内存的复杂场景。
heap_2.c基于单向链表管理固定大小的内存块,可动态分配和释放内存。对于固定大小内存块的分配和释放操作相对简单高效,适用于内存块大小固定的频繁分配和释放场景,如相同大小任务栈的管理。只能处理固定大小的内存块,存在内存碎片风险,当内存块大小需求不一致时,内存利用率可能较低。
heap_3.c对标准C库的malloc()free()函数进行简单包装。利用标准C库的功能,易于理解和移植,在熟悉标准C库内存管理的情况下可以快速上手。依赖标准C库的性能和特性,可能存在标准C库本身的内存碎片问题,对一些资源受限的嵌入式系统可能不太适用。
heap_4.c采用双向链表管理可变大小的内存块,能合并相邻空闲内存块来提高利用率,可动态分配和释放。能灵活处理不同大小的内存块分配,通过合并空闲内存块提高了内存利用率,适用于复杂多变的内存分配需求。实现相对复杂,占用一定的系统资源用于管理内存链表,内存分配和释放操作可能比简单的方法耗时。
heap_5.c基于heap_4.c的算法扩展到多个不连续的内存区域,可在这些区域间分配和释放内存。能够有效利用分散的内存资源,适用于内存分布不连续的系统,提高了整个系统的内存可用性。管理多个区域的内存增加了复杂性,对内存管理的开销进一步增大,实现和调试难度较高。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
字节对齐的目的是什么?
在这里插入图片描述

xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

1、heap_1 内存分配方法

动 态 内 存 分 配 需 要 一 个 内 存 堆 , FreeRTOS 中 的 内 存 堆 ucHeap[] , 大 小 为configTOTAL_HEAP_SIZE,这个前面讲 FreeRTOS 配置的时候就讲过了。不管是哪种内存分配方法,它们的内存堆都为 ucHeap[] , 而且大小都是 configTOTAL_HEAP_SIZE。内存堆在文件heap_x.c (x 为 1~5) 中定义的,比如 heap_1.c 文件:

#if( configAPPLICATION_ALLOCATED_HEAP == 1 ) extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //需要用户自行定义内存堆 //当宏 configAPPLICATION_ALLOCATED_HEAP 为 1 的时候需要//用户自行定义内存堆,否则的话由编译器来决定,默认都是由编译器//来决定的。如果自己定义的话就可以将内存堆定义//到外部 SRAM 或者 SDRAM 中
#else static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //编译器决定 
#endif 

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

适用于那些一旦创建好任务、信号量和队列就再也不会删除的应用,实际上大多数的FreeRTOS 应用都是这样的,代码实现和内存分配过程都非常简单,内存是从一个静态数组中分配到的,也就是适合于那些不需要动态内存分配的应用

void *pvPortMalloc( size_t xWantedSize ) 
{ 	void *pvReturn = NULL; static uint8_t *pucAlignedHeap = NULL; //确保字节对齐 #if( portBYTE_ALIGNMENT != 1 ) //(1) 这是一个条件编译指令。只有当 portBYTE_ALIGNMENT //不等于 1 时,才会编译 #if 和 #endif 之间的代码{ if( xWantedSize & portBYTE_ALIGNMENT_MASK ) //(2) 这里使用按位与操作符 & 来检查 xWantedSize //与 portBYTE_ALIGNMENT_MASK 按位与的结果是否不为零。//如果结果不为零,说明 xWantedSize 不是 portBYTE_ALIGNMENT 的整数倍,//需要进行字节对齐{ //需要进行字节对齐 xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); //(3) 这一步是进行字节对齐的核心操作。xWantedSize & portBYTE_ALIGNMENT_MASK //得到 xWantedSize 对 portBYTE_ALIGNMENT 取模的结果,即当前 xWantedSize //距离下一个 portBYTE_ALIGNMENT 整数倍的差值。然后用 portBYTE_ALIGNMENT //减去这个差值,得到需要增加的字节数,最后将这个增加的字节数加到 xWantedSize 上,//从而实现字节对齐} } #endif vTaskSuspendAll(); //(4) { if( pucAlignedHeap == NULL ) { //确保内存堆的开始地址是字节对齐的 pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE )\ //(5) &ucHeap[ portBYTE_ALIGNMENT ] ) &\ ( ~( ( portPOINTER_SIZE_TYPE )\ portBYTE_ALIGNMENT_MASK ) ) ); } //检查是否有足够的内存供分配,有的话就分配内存 if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && //(6) ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) ) { pvReturn = pucAlignedHeap + xNextFreeByte; //(7) xNextFreeByte += xWantedSize; //(8) } traceMALLOC( pvReturn, xWantedSize ); } ( void ) xTaskResumeAll(); //(9) #if( configUSE_MALLOC_FAILED_HOOK == 1 ) //(10) { if( pvReturn == NULL ) { extern void vApplicationMallocFailedHook( void ); vApplicationMallocFailedHook(); } } #endif return pvReturn; //(11) 
} 

补充:
portBYTE_ALIGNMENT 是一个在特定编程环境(尤其是嵌入式系统或与硬件交互紧密的代码中)经常使用的宏定义或常量。它用于指定字节对齐的规则,即数据存储时按照多少字节的边界进行对齐;

指定对齐单位:它的值表示数据在内存中存储时需要对齐的字节数。例如,如果 portBYTE_ALIGNMENT 被定义为 4,意味着数据存储时会按照 4 字节的边界进行对齐。这通常是为了满足特定硬件架构对数据访问的要求,不同的硬件架构可能对数据的对齐方式有不同的规定,以提高内存访问的效率和稳定性

条件编译与对齐处理:在代码中,常通过 portBYTE_ALIGNMENT 来进行条件编译和字节对齐的逻辑判断。就像你提供的代码中,通过检查 portBYTE_ALIGNMENT 是否不等于 1 来决定是否执行后续的字节对齐操作。如果 portBYTE_ALIGNMENT 等于 1,说明不需要进行特殊的字节对齐处理,因为所有数据默认已经按 1 字节对齐;而当 portBYTE_ALIGNMENT 大于 1 时,需要对数据大小进行调整,使其满足指定的对齐要求。

portBYTE_ALIGNMENT_MASK 的作用
portBYTE_ALIGNMENT_MASK 是一个与 portBYTE_ALIGNMENT 相关的掩码值。通常,portBYTE_ALIGNMENT 是 2 的幂次方,例如 2、4、8 等。当 portBYTE_ALIGNMENT 是 2 的幂次方时,portBYTE_ALIGNMENT_MASK 的值为 portBYTE_ALIGNMENT - 1
例如:
如果 portBYTE_ALIGNMENT 为 4(二进制 100),那么 portBYTE_ALIGNMENT_MASK 为 3(二进制 011)。
如果 portBYTE_ALIGNMENT 为 8(二进制 1000),那么 portBYTE_ALIGNMENT_MASK 为 7(二进制 0111)。
在这里插入图片描述

在这里插入图片描述

2、heap_2 内存分配方法

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

3、heap_3 内存分配方法

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

4、heap_4 内存分配方法

heap_4 提供了一个最优的匹配算法,不像 heap_2,heap_4会将内存碎片合并成一个大的可用内存块,它提供了内存块合并算法。内存堆为ucHeap[ ],大小同样为configTOTAL_HEAP_SIZE。

可以通过函数xPortGetFreeHeapSize() 来获取剩余的内存大小

在这里插入图片描述
它采用双向链表结构来管理内存,并能够合并相邻的空闲内存块。这使得内存的利用率得到提高。在资源有限的嵌入式系统中,高效的内存利用至关重要。比如,当一个任务结束并释放其占用的内存块后,heap_4.c 可以将该内存块与相邻的空闲内存块合并为一个更大的空闲内存块,以便后续分配给需要较大内存空间的其他任务或资源。

5、heap_5内存分配算法

heap5 内存管理算法是在 heap4 内存管理算法的基础上实现的,但是 heap5 内存管理算法在 heap4 内存管理算法的基础上实现了管理多个非连续内存区域的能力。

heap_5 内存管理算法默认并没有定义内存堆,需要用户手动指定内存区域的信息,对其进行初始化。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【设计模式】介绍常见的设计模式

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 ✨ 介绍一下常见的设计模式✨ Spring 中常见的设计模式 这期内容主要是总结一下常见的设计模式&#xff0c;可…

6 分布式限流框架

限流的作用 在API对外互联网开放的情况下&#xff0c;是无法控制调用方的行为的。当遇到请求激增或者黑客攻击的情况下&#xff0c;会导致接口占用大量的服务器资源&#xff0c;使得接口响应效率的降低或者超时&#xff0c;更或者导致服务器宕机。 限流是指对应用服务进行限制…

【动态规划篇】欣赏概率论与镜像法融合下,别出心裁探索解答括号序列问题

本篇鸡汤&#xff1a;没有人能替你承受痛苦&#xff0c;也没有人能拿走你的坚强. 欢迎拜访&#xff1a;羑悻的小杀马特.-CSDN博客 本篇主题&#xff1a;带你解答洛谷的括号序列问题&#xff08;绝对巧解&#xff09; 制作日期&#xff1a;2025.01.10 隶属专栏&#xff1a;C/C题…

数据库高安全—角色权限:权限管理权限检查

目录 3.3 权限管理 3.4 权限检查 书接上文数据库高安全—角色权限&#xff1a;角色创建角色管理&#xff0c;从角色创建和角色管理两方面对高斯数据库的角色权限进行了介绍&#xff0c;本篇将从权限管理和权限检查方面继续解读高斯数据库的角色权限。 3.3 权限管理 &#x…

深入浅出负载均衡:理解其原理并选择最适合你的实现方式

负载均衡是一种在多个计算资源&#xff08;如服务器、CPU核心、网络链接等&#xff09;之间分配工作负载的技术&#xff0c;旨在优化资源利用率、提高系统吞吐量和降低响应时间。负载均衡的实现方式多种多样&#xff0c;以下是几种常见的实现方式&#xff1a; 1. 硬件负载均衡&…

Training-free regional prompting for diffusion transformers

通过语言模型来构建位置关系的,omnigen combine来做位置生成,其实可以通过大模型来做,不错。 1.introduction 文生图模型在准确处理具有复杂空间布局的提示时仍然面临挑战,1.通过自然语言准确描述特定的空间布局非常困难,特别是当对象数量增加或需要精确的位置控制时,2.…

麦田物语学习笔记:背包物品选择高亮显示和动画

如题,本篇文章没讲动画效果 基本流程 1.代码思路 (1)先用点击事件的接口函数去实现,点击后反转选择状态(isSelected),以及设置激活状态(SetActive),并且还需要判断该格子是否为空,空格子是点不动的,完成后以上后,出现的问题是高亮应该是有且仅有一个格子是高亮的,而现在可以让…

Linux:深入了解fd文件描述符

目录 1. 文件分类 2. IO函数 2.1 fopen读写模式 2.2 重定向 2.3 标准文件流 3. 系统调用 3.1 open函数认识 3.2 open函数使用 3.3 close函数 3.4 write函数 3.5 read函数 4. fd文件描述符 4.1 标准输入输出 4.2 什么是文件描述符 4.3 语言级文件操作 1. 文件分类…

数据结构:栈(Stack)和队列(Queue)—面试题(一)

目录 1、括号匹配 2、逆波兰表达式求值 3、栈的压入、弹出序列 4、最小栈 1、括号匹配 习题链接https://leetcode.cn/problems/valid-parentheses/description/ 描述&#xff1a; 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] …

51单片机(一) keil4工程与小灯实验

直接开始 新建一个工程 在这里插入图片描述 添加文件 另存为 添加文件到组 写下一个超循环系统代码 调整编译项编译 可以在工程目录找到编译好的led_fst.hex 自行烧写到各自的开发板。 会看到什么都没有。 现在定义一个GPIO端口与小灯的连接&#xff0c;再点亮小灯…

基于 Python 和 OpenCV 的人脸识别上课考勤管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Vue2:el-table中的文字根据内容改变颜色

想要实现的效果如图,【级别】和【P】列的颜色根据文字内容变化 1、正常创建表格 <template><el-table:data="tableData"style="width: 100%"><el-table-column prop="id" label="ID"/> <el-table-column …

案例研究:UML用例图中的结账系统

在软件工程和系统分析中&#xff0c;统一建模语言&#xff08;UML&#xff09;用例图是一种强有力的工具&#xff0c;用于描述系统与其用户之间的交互。本文将通过一个具体的案例研究&#xff0c;详细解释UML用例图的关键概念&#xff0c;并说明其在设计结账系统中的应用。 用…

73.矩阵置零 python

矩阵置零 题目题目描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a; 题解思路分析Python 实现代码代码解释提交结果 题目 题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例…

C++【深入底层,从零模拟实现string类】

在学习了类和对象、模板等前期的C基础知识之后&#xff0c;我们可以尝试根据C标准库中所提供的接口类型&#xff0c;来搭建我们自己的string类型。这个过程有助于初学者掌握C的基础语法及底层逻辑。 框架的搭建 首先搭建模型的基础框架&#xff0c;需要建立my_string.h和my_st…

切忌 SELECT *,就算表只有一列

原文地址 尽量避免 SELECT *&#xff0c;即使在单列表上也是如此 – 如果你现在不同意这一点&#xff0c;读完这篇文章&#xff0c;你可能就要动摇了。 2012年的一个故事 这是我 12 年前&#xff08;约 2012-2013 年&#xff09;在客户后台应用程序中遇到的一个真实故事。 当…

DEV C++软件下载

一、进入网站 https://sourceforge.net/projects/orwelldevcpp/ 二、点击下载 三、安装步骤 1、点击 “OK” 2、点击“I agree” 3、点击“Next” 4、按步骤切换路径&#xff0c;本文选在D盘&#xff0c;可自行选取文件路径 5、等待安装 6、点击完成 7、选择语言 8、点击“N…

OpenBSD之安装指南

安装介质下载 OpenBSD的官网下载地址&#xff1a;https://www.openbsd.org/faq/faq4.html#Download&#xff0c;同时也是《OpenBSD FAQ - Installation Guide》。长篇大论了很多&#xff0c;每一个章节都能看懂是干嘛的&#xff0c;连起来就容易晕。并且是英文的&#xff0c;要…

Vue.config.productionTip = false 不起作用的问题及解决

文章目录 一、问题描述二、解决方法 一、问题描述 当我们在代码页面上引入Vue.js(开发版本)时&#xff0c;运行代码会出现以下提示&#xff0c;这句话的意思是&#xff1a;您正在开发模式下运行Vue&#xff0c;在进行生产部署时&#xff0c;请确保打开生产模式 You are runni…

C#,图论与图算法,输出无向图“欧拉路径”的弗勒里(Fleury Algorithm)算法和源程序

1 欧拉路径 欧拉路径是图中每一条边只访问一次的路径。欧拉回路是在同一顶点上开始和结束的欧拉路径。 这里展示一种输出欧拉路径或回路的算法。 以下是Fleury用于打印欧拉轨迹或循环的算法&#xff08;源&#xff09;。 1、确保图形有0个或2个奇数顶点。2、如果有0个奇数顶…