【freeRTOS】操作系统之五.-内存管理

news2024/12/23 10:03:13

\FreeRTOS\Source\portable\MemMang下提供了5中内存分配机制的实现

一、配置FreeRTOS内存大小

在FreeRTOSConfig.h头文件中宏configTOTAL_HEAP_SIZE用于配置内核可用的RAM大小。

在heap1.c, heap2.c, heap4.c源文件中,分配的内存实际上是用一个静态数组ucHeap来表示。

再具体的实现上,FreeRTOS 内核规定的几个内存管理函数原型。系统内部及用户如果要使用内存,只能通过该函数接口进行申请。因此完全可以有用户自己实现。具体函数接口如下(不同方案稍有区别):

  • void *pvPortMalloc( size_t xSize ) :内存申请函数
  • void vPortFree( void *pv ) :内存释放函数
  • void vPortInitialiseBlocks( void ) :初始化内存堆函数
  • size_t xPortGetFreeHeapSize( void ):获取当前未分配的内存堆大小
  • size_t xPortGetMinimumEverFreeHeapSize( void ):获取未分配的内存堆历史最小值

heap_1.c

​ heap1.c是FreeRTOS提供的多种内存分配策略中最简单的一种,其使用一个全局静态变量xNextFreeByte来记录未分配使用的内存空间的位置,每分配一次就会往后进行偏移。另外,在这种方式中,已经分配的内存不会再释放。**实际上,大多数的嵌入式系统并不需要动态删除任务、信号量、队列等,而是在初始化的时候一次性创建好,便一直使用,永远不用删除。**所以这个内存管理策略实现简洁、安全可靠,使用的非常广泛。

​ 应用程序调用此函数void *pvPortMalloc(size_t xWantedSize)请求分配内存,需要注意的是函数中有两个地方为满足字节对齐的要求进行调整:

为确保从静态数组ucHeap中分配给应用程序的内存块是8字节对齐,函数会判断xWantedSize是否为8的倍数,如果不是,就会添补字节。

step 1 :初始化

初始化分配一片内存:

img

step 2 :分配

用户分配之后的内存

img

​ 由于分配出去的内存空间不需要回收,因此每一次分配空间的时候只需要按需要的内存大小在空闲空间上分割出来就可以了。分割时,首先要检查需要的内存大小有没有超出空闲空间的大小,还要检查假如分配完空间后,其末地址是否溢出。假如没有超出空闲空间大小,出没有发生内存溢出现象,才进行分配,记录新分配空间的首地址到pvReturn。

heap_2.c

​ Heap_1.c 是FreeRTOS多种内存管理机制中最简单的一种:全局声明一个静态数组ucHeap,然后通过指针偏移记录空间的分配情况,在这种内存机制下无法对内存进行释放。

​ 在heap2.c中,同样使用一个全局静态数组ucHeap来表示内存,heap2.c内存管理机制较heap1.c而言增加了内存释放的功能,通过使用链表对内存进行有效管理。

​ heap2.c虽然支持内存回收,但是回收内存时不进行相邻空闲块的合并,因此这种策略会导致内存碎片,系统运行久了会出现无法分配过大的连续空间的情况,heap4.c中内存管理机制就是为了弥补这种缺陷而诞生的,它在heap2.c的基础上增加了连续空闲块合并的功能。

​ 由于FreeRTOS用空闲块对内存堆进行管理,于是用这一个结构来形成一条空闲块链表对空闲块进行组织和管理。

step1: 初始化

初始化一片内存

img

初始化完成后的内存分布:

img

step 2 :分配

用户申请一片内存空间

img

step 3 :释放

用户释放一片内存

img

​ 要是分配出去的空闲块的剩余空间要是比两倍的空闲块头还要大,则将分配出去的这个空闲块分割剩余的空间出来,重新放到空闲块链表中。例如,初始化后只有一个空闲块,这个空闲块大小为17KB。经过调整后的用户申请空间大小为1KB,则FreeRTOS就从这个空闲块靠近块首的地方分割出1KB出来分配出去,剩余的16KB则重新放回空闲块链表里。以便下一次继续分配。

img

heap_3.c

​ 该内存管理方式是将标准的malloc()和free()进行了简单的封装。只是在封装的时候加入了调度器挂起和恢复操作。前两种方式内存管理是在系统申请的一个大数组中,该种方式是真正在堆中进行的。挂起和恢复使线程安全。

因此,在MCU(STM32…)的启动文件(即汇编阶段)就要设置分配堆的大小。

临界区:

进入临界区即屏蔽中断,但这里不屏蔽所有中断,FreeRTOS有一个临界区中断优先级,在FreeRTOSConfig.h里配置。

#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191

​ 关闭中断时设置BASEPRI寄存器为该值,这个寄存器会并屏蔽掉掉优先级比设定值低的,而优先级高的不受影响,如果BASEPRI设为0则不屏蔽任何中断。
假如M3的中断优先级为3位,则191(0b10111111)的前3位有效,即优先级为5,此时优先级为57的中断优先级低于设定值值会被屏蔽,而优先级为04的中断优先级高于设定值不受影响,不受影响的中断称作非临界区中断,不能调用FreeRTOS的API,否则会破会系统数据。
进入临界区时会屏蔽临界区内的所有中断,进入和离开临界区的API需要成对使用。

taskENTER_CRITICAL()
......
taskEXIT_CRITICAL() 

heap_4.c

​ FreeRTOS 中的 heap 4 内存管理,可以算是 heap 2 的增强版本。每次内存分配后都会产生一个内存块,多次分配后,会产生很多内存碎片,在较为复杂的场景(需要经常动态分配和释放场景)下,几乎是无法胜任。所以就有了 heap 4,它相比 heap 2 来说,提供了相邻空闲的内存块合并的功能,一定程度上减少了内存碎片,使得释放了的内存能够再度合并称为较为大的内存块。

​ Heap_4用了BlockLink_t中xBlockSize的最高一位来标识某个内存块是否处于空闲状态

​ 在实现原理上和 heap 2大多一样,在插入这块进行检查,在 heap 2中,也有这个函数,但是它是按照内存块的小到大进行排序,由于有相邻内存块合并的要求,所以在 heap 4 中,内存块链表的组织形式,是按照他们的地址顺序由小到大组织的。首先通过 xStart 开始,获取第一个空闲块的地址,并比较它的下一个空闲块地址和待插入空闲块地址的大小,以便获得合适的插入位置,获得合适的位置了以后,判断待插入的这个空闲块是否和前一个空闲块相邻,如果相邻的话,就将两个块合并到一起。然后在判断是否和后一个空闲块也相邻,如果相邻,也将他们合并。

多次分配后内存情况:

img

img

如果要释放中间那个内存,那么就会触发向上和向下的合并:

img

heap_5.c

​ heap5与heap4分配释放算法完全相同,只是heap5支持管理多块不连续的内存,本质是将多块不连续内存用链表串成一整块内存,再用heap4算法来分配释放。若使用heap5则在涉及到分配释放的函数调用时要先调用vPortDefineHeapRegions把多块不连续内存串成一块初始化。

img

使用:

#define RAM1_START_ADDRESS ( ( uint8_t * ) 0x00010000 )
#define RAM1_SIZE ( 65 * 1024 )
 
#define RAM2_START_ADDRESS ( ( uint8_t * ) 0x00020000 )
#define RAM2_SIZE ( 32 * 1024 )
 
#define RAM3_START_ADDRESS ( ( uint8_t * ) 0x00030000 )
#define RAM3_SIZE ( 32 * 1024 )
 
/* Create an array of HeapRegion_t definitions, with an index for each of the three 
RAM regions, and terminating the array with a NULL address. The HeapRegion_t 
structures must appear in start address order, with the structure that contains the 
lowest start address appearing first. */
const HeapRegion_t xHeapRegions[] =
{
    { RAM1_START_ADDRESS, RAM1_SIZE },
    { RAM2_START_ADDRESS, RAM2_SIZE },
    { RAM3_START_ADDRESS, RAM3_SIZE },
    { NULL, 0 } /* Marks the end of the array. */
};
int main( void ) {
    /* Initialize heap_5. */
    vPortDefineHeapRegions( xHeapRegions );
  
}

需要注意的几点是:

1、定义 HeapRegion_t 数组的时候,最后一定要定义成为 NULL 和 0,这样接口才知道这是终点;

2、被定义的 RAM 区域,都会去参与内存管理;

类似:

 HeapRegion_t xHeapRegions[] =
 {
	 { ( uint8_t * ) 0x80000000UL, 0x10000 }, 
	 { ( uint8_t * ) 0x90000000UL, 0xa0000 }, 
	 { NULL, 0 }        << Terminates the array.
};

 

也可以:

const HeapRegion_t xHeapRegions[] =
{
    { ucHeap, RAM1_HEAP_SIZE },
    { RAM2_START_ADDRESS, RAM2_SIZE },
    { RAM3_START_ADDRESS, RAM3_SIZE },
    { NULL, 0 } /* Marks the end of the array. */
};

这样ram1 的地址就安全了。

img

  1. 内存越界?

传送门:
【freeRTOS】操作系统之一-任务调度
【freeRTOS】操作系统之二-队列
【freeRTOS】操作系统之三-信号量
【freeRTOS】操作系统之四-事件标志组
【freeRTOS】操作系统之五.-内存管理
【freeRTOS】操作系统之六-低功耗模式
【freeRTOS】操作系统之七-freeRtos移植

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里后端优化这么恐怖?看完这20W字Java性能实战经验手册,最少P7

学习是一种基础性的能力。然而&#xff0c;“吾生也有涯&#xff0c;而知也无涯。”&#xff0c;如果学习不注意方法&#xff0c;则会“以有涯随无涯&#xff0c;殆矣”。 学习就像吃饭睡觉一样&#xff0c;是人的一种本能&#xff0c;人人都有学习的能力。我们在刚出生的时候…

通过rpm安装MySQL8.0

前文 下载虚拟机&#xff0c;安装Linux&#xff08;Centos7发行版&#xff09;关闭SELinux服务 &#xff08;安全模块&#xff0c;容易跟其他程序冲突&#xff0c;建议关闭&#xff09;替换yum源 &#xff08;加快下载依赖速度&#xff09;安装MySQL 关闭SELinux 进入vi /ec…

代码随想录——冗余连接(并查集)

题目 树可以看成是一个连通且 无环 的 无向 图。 给定往一棵 n 个节点 (节点值 1&#xff5e;n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间&#xff0c;且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges &#xff0c;edges[…

(C语言)成绩统计

[#4练习赛]成绩统计 题目描述 传智专修学院的“Java程序设计”班有 N(1≤100)N(1 \le 100)N(1≤100) 个学生&#xff0c;知道他们的个人信息和成绩&#xff1a; 姓名&#xff08;长度不超过 202020 的仅由小写字母组成的字符串&#xff09;平时分成绩&#xff08;000 到 100…

网格窗帘布能符合NFP92-503法国M2级别要求吗?

NFP 92-503是针对建筑材料类产品的阻燃性能测试&#xff0c;主要分为M1-M4四个级别&#xff0c;M1为最高级别&#xff0c;以此类推&#xff0c;故而M2对阻燃要求还是比较严格的&#xff0c;布料要申请M2要求的话&#xff0c;前提至少要有阻燃处理了。 NFP 92-503 建筑材料阻燃测…

振弦采集模块主动上传测量数据( UART)

振弦采集模块主动上传测量数据&#xff08; UART&#xff09; 默认情况下 VMXXX 模块总是以从机身份与主机完成数据交互&#xff0c; 在这种主从结构中&#xff0c; VMXXX 从不主动上传数据&#xff0c; 可通过修改自动上传寄存器&#xff08; ATSD_SEL&#xff09;来实现模块主…

论文阅读笔记 | 三维目标检测——MV3D算法

如有错误&#xff0c;恳请指出。 文章目录1. 背景2. 网络结构2.1 提案投影网络2.2 区域融合网络3. 实验结果paper&#xff1a;《Multi-View 3D Object Detection Network for Autonomous Driving》 1. 背景 激光雷达可以提供物体的深度信息&#xff0c;而摄像头可以提供物体的…

麦子-linux字符设备驱动初探

字符驱动的联系 框架 软件支持&#xff1a;驱动 驱动类型 1、字符驱动&#xff08;每次读取一个字节&#xff09; 2、块驱动&#xff08;每次读取多个字节&#xff09; 3、网络设备驱动&#xff08;协议栈&#xff09; 设备号dev_t 内核中使用类型dev_t来描述设备号&…

【Unity3D】Unity 脚本 ④ ( 游戏物体 GameObject 的坐标 | 修改 游戏物体 GameObject 的本地坐标 )

文章目录一、 游戏物体 GameObject 的坐标二、 修改 游戏物体 GameObject 的本地坐标一、 游戏物体 GameObject 的坐标 游戏物体 GameObject 的坐标 有 2 种 : 世界坐标 : 调用 Transform#position 属性 获得 ;本地坐标 : 调用 Transform#localPosition 属性 获得 ,本地坐标 就…

这份Java大厂常见面试题解析,你的闭关修练宝典!

纵观今年的技术招聘市场&#xff0c; Java依旧是当仁不让的霸主 &#xff01;即便遭受 Go等新兴语言不断冲击&#xff0c;依旧岿然不动。究其原因&#xff1a; Java有着极其成熟的生态&#xff0c;这个不用我多说&#xff1b; Java在 运维、可观测性、可监 控性方面都有着非常…

Materials Studio8.0

centos7.9 检查主机名&#xff1a; 检查环境&#xff1a; [rootmaster conf]# rpm -qa|grep glibc; rpm -qa|grep libgcc; rpm -qa|grep libstdc ;rpm -qa|grep redhat-lsb Materials Studio8.0软件包 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;fomg 软件…

【LeetCode每日一题:891. 子序列宽度之和~~~排序+数学推导】

题目描述 一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。 给你一个整数数组 nums &#xff0c;返回 nums 的所有非空 子序列 的 宽度之和 。由于答案可能非常大&#xff0c;请返回对 109 7 取余 后的结果。 子序列 定义为从一个数组里删除一些&#xff08;或者…

骗赞小程序(仅供恶搞)

作为一个 良 凉心博主&#xff0c;平时发的文章得不到点赞&#xff0c;也得不到关注&#xff0c;这改如何办&#xff1f;很简单只需要勤奋发文 威逼利诱。如何威逼利诱&#xff0c;用关机啊&#xff01; 所以&#xff0c;我们需要一个程序&#xff0c;让用户自行选择是否进行关…

85服务-ControlDTCSetting

诊断协议那些事儿 诊断协议那些事儿专栏系列文章&#xff0c;本文介绍诊断和通讯管理功能单元下的85服务ControlDTCSetting&#xff0c;该服务的目的是控制DTC设置。 文章目录诊断协议那些事儿一、85服务-ControlDTCSetting二、请求格式DTCSettingType三、肯定响应格式四、否…

PLC滑动平均值滤波(SMART 200梯形图篇)

滑动平均值滤波的算法原理请参看《信号处理》专栏的其它文章,这里给出简单快速写法,以供参考。 S7-200 SMART PLC信号处理系列之滑动平均值滤波FB_RXXW_Dor的博客-CSDN博客_plc平均值滤波程序PLC相关滤波算法,专栏有很多详细讲解这里不再赘述。滑动平均值滤波和算术平均值滤…

基于51单片机的多功能视力保护台灯

目录 摘要. 1 1弓I言 1 1.1 选 题 背 景 1 2 视力保护器的现状 2 1.3 本文主要研究内容 3 2系统硬件 设计 31 89c52 单片机简介 4 2.2 超声波测距报警 电路 53 89c5 2 单片机的中断系统 74 光线报警电路 8 1 光敏电阻控制电路 8 2.5 定时报菩电路 9 2.5. 1 单片机控制电路 10 …

[附源码]SSM计算机毕业设计茶园文化交流平台论文JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

u-boot对设备树的支持__传递dtb给内核

a. u-boot中内核启动命令: bootm <uImage_addr> // 无设备树,bootm 0x30007FC0 bootm <uImage_addr> <initrd_addr> <dtb_addr> // 有设备树 比如 : nand read.jffs2 0x30007FC0 kernel; // 读内核u…

两客一危解决方案-最新全套文件

两客一危解决方案-最新全套文件一、建设背景二、思路架构三、建设方案1、多维数据融合2、车辆轨迹分析3、AI 视频分析4、评价体系四、获取 - 两客一危全套最新解决方案合集一、建设背景 “两客一危一货”车辆是指公路客运、旅游客运、危化品运输车及货运车辆。“两客一危”车辆…

【freeRTOS】操作系统之三-信号量

1 二值信号量 1.1 二值信号量简介 ​ 二值信号量通常用于互斥访问或同步&#xff0c;二值信号量和互斥信号量非常类似&#xff0c;但是还是有一 些细微的差别&#xff0c;互斥信号量拥有优先级继承机制&#xff0c;二值信号量没有优先级继承。因此二值信号量更适合用于同步(任…