FreeRTOS的内存管理方法(超详细)

内存管理

        我们知道每次创建任务、队列、互斥锁、软件定时器、信号量或事件组时,RTOS 内核都需要 RAM , RAM 可以从 RTOS API 对象创建函数内的 RTOS 堆自动动态分配, 或者由应用程序编写者提供

        如果 RTOS 对象是动态创建的,那么标准 C 库 malloc() 和 free() 函数有时可用于此目的,但是...它们在嵌入式系统上并不总是可用,占用了宝贵的代码空间,不是线程安全的,而且不是确定性的 (执行函数所需时间将因调用而异),所以更多的时候需要的不是一个替代的内存分配实现。

        一个嵌入式/实时系统的 RAM 和定时要求可能与另一个非常不同,所以单一的 RAM 分配算法 将永远只适用于一个应用程序子集。

        为了避免此问题,FreeRTOS 将内存分配 API 保留在其可移植层。 可移植层在实现核心 RTOS 功能的源文件之外, 允许提供适合于正在开发的实时系统的特定应用程序实现。 当 RTOS 内核需要 RAM 时,它不调用 malloc(),而是调用 pvPortMalloc()。 释放 RAM 时, RTOS 内核调用 vPortFree(),而不是 free()。

        FreeRTOS 提供了几种堆管理方案, 其复杂性和功能各不相同。 你也可以提供自己的堆实现, 甚至同时使用两个堆实现。 同时使用两个堆实现 允许将任务堆栈和其他 RTOS 对象放置在 内部 RAM 中,并将应用程序数据放置在较慢的外部 RAM 中。

5种管理方法

        源码中默认提供了5个文件,对应内存管理的5种方法。每个提供的实现都包含在单独的源文件中 (分别是 heap_1.c、 heap_2.c、heap_3.c、heap_4.c 和 heap_5.c), 位于主 RTOS 源代码下载内容的 Source/Portable/MemMang 目录下。 可根据需要添加其他实现方式。 每次一个项目中, 只应包含其中一个源文件[这些可移植层函数定义的堆 将由 RTOS 内核使用, 即使使用 RTOS 的应用程序选择使用自己的堆实现]。

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

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

heap_3 —— 简单包装了标准 malloc() 和 free(),以保证线程安全。

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

heap_5 —— 如同 heap_4,能够跨越多个不相邻内存区域的堆。

注意:

        heap_1 不太有用,因为 FreeRTOS 添加了静态分配支持。

        heap_2 现在被视为旧版,因为较新的 heap_4 实现是首选。

heap_1.c

        heap_1 不太有用,因为 FreeRTOS 添加了静态分配支持。heap_1 是最简单的实现方式。 内存一经分配,它不允许内存再被释放 。 尽管如此,heap_1.c 还是适用于大量嵌入式应用程序。 这是因为许多小型和深度嵌入的应用程序在系统启动时 创建了所需的所有任务、队列、信号量等, 并在程序的生命周期内使用所有这些对象 (直到应用程序再次关闭或重新启动)。 任何内容 都不会被删除。

        这个实现只是在要求使用 RAM 时将一个单一的数组细分为更小的块 。 数组的总大小(堆的总大小)通过 configTOTAL_HEAP_SIZE (定义于 FreeRTOSConfig.h 中)设置 。 提供了 configAPPLICATION_ALLOCATED_HEAP FreeRTOSConfig.h 配置常量, 以允许将堆放置在内存中的特定地址。

        xPortGetFreeHeapSize() API 函数返回未分配的堆空间总量,允许优化 configTOTAL_HEAP_SIZE 设置。

heap_1 实现

        如果您的应用程序从未删除任务、队列、信号量、互斥锁等,则可以使用。 (这实际上涵盖了使用 FreeRTOS 的大多数应用程序)。

        始终具有确定性(总是需要相同的时间来执行), 不会导致内存碎片化。非常简单,且从静态分配的数组分配内存, 这意味着它通常适合用于不允许真实动态内存分配的应用程序 。

使用heap_1时,内存分配过程如下图所示:

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

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

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

heap_2.c

        heap_2 现在被视为旧版,首选 heap_4 。heap_2 使用最佳适应算法,并且与方案 1 不同,它允许释放先前分配的块, 它 不 将相邻的空闲块组合成一个大块。 有关不合并空闲块的实现,请参阅 heap_4.c。

        可用堆空间的总量通过 configTOTA L_HEAP_SIZE(定义于 FreeRTOSConfig.h 中)设置。 提供了 configAPPLICATION_ALLOCATED_HEAP FreeRTOSConfig.h 配置常量, 以允许将堆放置在内存中的特定地址。xPortGetFreeHeapSize() API 函数返回未分配的堆空间总量, (允许优化 configTOTAL_HEAP_SIZE 设置), 但不提供关于未分配的内存如何被碎片化成小块的信息 。

        pvPortCalloc() 函数的签名与标准库 calloc 函数相同。它为一个对象数组分配内存, 并将分配的存储空间中的所有字节初始化为零。如果分配成功, 它会返回指向分配的内存块中最低字节的指针。如果分配失败,它会返回一个空指针。

heap_2实现

        即使应用程序重复删除任务、队列、 信号量、互斥锁等,仍然可用, 但要注意以下关于内存碎片化的信息。如果正在分配和释放的内存为随机大小,则不可使用 。

例如:

        如果应用程序动态地创建和删除任务, 且分配给正在创建任务的堆栈大小总是相同的 , 那么 heap2.c 可以在大多数情况下使用。 但是, 如果分配给正在创建任务的堆栈的大小不是总相同, 那么可用的空闲内存可能会被碎片化成许多小块 , 最终导致分配失败。 在这种情况下,heap_4.c 是更好的选择。

        如果应用程序动态地创建和删除任务, 且队列存储区域在每种情况下都是相同的 (队列存储区域是队列项大小乘以队列长度), 那么 heap_2.c 可以在大多数情况下使用。 但是, 如果在每种情况下的队列存储区域不相同, 那么可用的空闲内存可能会被碎片化成许多小块 , 最终导致分配失败。 在这种情况下,heap_4.c 是更好的选择。

        应用程序直接调用 pvPortMalloc() 和 vPortFree(), 而不是仅通过其他 FreeRTOS API 函数间接调用。

        如果您应用程序的队列、任务、信号量、互斥锁等的顺序不可预测, 可能会导致内存碎片化。 这对几乎所有的应用程序来说都是不可能的, 但应牢记这一点。非确定性,但比大多数标准 C 库 malloc 实现更有效。

        heap_2.c 适用于许多必须动态创建对象的小型实时系统 。 请参阅 heap_4 了解类似实现, 该实现将空闲的内存块组合成单一的大内存块。

使用heap_2时,内存分配过程如下图所示:

A:创建了3个任务

B:删除了一个任务,空闲内存有3部分:顶层的、被删除任务的TCB空间、被删除任务的Stack空间

C:创建了一个新任务,因为TCB、栈大小跟前面被删除任务的TCB、栈大小一致,所以刚好分配到原来的内存

heap_3.c

        这是标准 C 库 malloc() 和 free() 函数实现了简单的包装器, 在大多数情况下,将与您选择的编译器一起提供。 该 包装器只是使 malloc() 和 free() 函数线程安全。

heap_3实现

        需要链接器设置堆,需要编译器库提供 malloc() 和 free() 实现。不具有确定性,能会大大增加 RTOS 内核代码大小。

        请注意,使用 heap_3 时,FreeRTOSConfig.h 中的 configTOTAL_HEAP_SIZE 设置无效 。

heap_4.c

         此方案使用第一适应算法,并且与方案 2 不同, 它确实将相邻的空闲内存块组成单个大内存块(它确实包含合并算法) 。

        可用堆空间的总量通过 configTOTAL_HEAP_SIZE (定义于 FreeRTOSConfig.h 中)设置。 提供了 configAPPLICATION_ALLOCATED_HEAP FreeRTOSConfig.h 配置常量, 以允许将堆放置在内存中的特定地址。

        xPortGetFreeHeapSize() API 函数被调用时返回未分配的堆空间总量, xPortGetMinimumEverFreeHeapSize() API 函数返回 FreeRTOS 应用程序启动的系统中已存在的最小空闲堆空间量。 这两个函数都没有提供关于未分配的 内存如何碎片化为小块的信息。

        vPortGetHeapStats() API 函数提供了其他信息。 它填充了一个 heap_t 结构体的成员,如下所示。

/* Prototype of the vPortGetHeapStats() function. */
void vPortGetHeapStats( HeapStats_t *xHeapStats );/* Definition of the Heap_stats_t structure. */typedef struct xHeapStats
{size_t xAvailableHeapSpaceInBytes;      /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */size_t xSizeOfLargestFreeBlockInBytes;     /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */size_t xNumberOfFreeBlocks;            /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */size_t xNumberOfSuccessfulAllocations;   /* The number of calls to pvPortMalloc() that have returned a valid memory block. */size_t xNumberOfSuccessfulFrees;     /* The number of calls to vPortFree() that has successfully freed a block of memory. */
} HeapStats_t;

        pvPortCalloc() 函数的签名与标准库 calloc 函数相同。它为一个对象数组分配内存, 并将分配的存储空间中的所有字节初始化为零。如果分配成功, 它会返回指向分配的内存块中最低字节的指针。如果分配失败,它会返回一个空指针。

heap_4使用

        即使应用程序重复删除任务、队列、 信号量、互斥锁等,仍然可用。

        与 heap_2 实现相比,导致堆空间严重碎片化成多个小块的可能性更小 (即使正在分配和释放的内存是随机大小) 。不具有确定性,但比大多数标准 C 库 malloc 实现更有效。

        heap_4.c 对于想在应用程序代码中直接使用可移植层内存分配方案的应用程序特别有用 (而不是 通过调用 API 函数 pvPortMalloc() 和 vPortFree() 来间接使用)。

        Heap_4执行的时间是不确定的,但是它的效率高于标准库的malloc、free。

heap_5.c

         此方案使用与 heap_4 相同的第一拟合和内存合并算法, 允许堆跨越多个不相邻(非连续) 内存区域。Heap_5 通过调用 vPortDefineHeapRegions() 初始化,且不得使用, 直到 vPortDefineHeapRegions() 执行以后。 创建 RTOS 对象(任务、队列、信号量等)将隐式调用 pvPortMalloc(),因此使用 heap_5 时,在创建任何此类对象之前调用 vPortDefineHeapRegions() 至关重要。

        vPortDefineHeapRegions() 采用单一参数, 该参数为 HeapRegion_t 结构体数组。 HeapRegion_t 在 portable.h 中定义为

typedef struct HeapRegion
{/* Start address of a block of memory that will be part of the heap.*/uint8_t *pucStartAddress;/* Size of the block of memory. */size_t xSizeInBytes;
} HeapRegion_t;The HeapRegion_t type definition

        数组使用空位零大小的区域定义终止, 数组中定义的内存区域必须按地址顺序, 从低地址到高地址显示。 以下源代码片段提供了示例 。 此外, MSVC Win32 模拟器演示也使用 heap_5 ,因此可作参考。

/* Allocate two blocks of RAM for use by the heap.  The first is a block of
0x10000 bytes starting from address 0x80000000, and the second a block of
0xa0000 bytes starting from address 0x90000000.  The block starting at
0x80000000 has the lower start address so appears in the array fist. */const HeapRegion_t xHeapRegions[] =
{{ ( uint8_t * ) 0x80000000UL, 0x10000 },{ ( uint8_t * ) 0x90000000UL, 0xa0000 },{ NULL, 0 } /* Terminates the array. */
};
/* Pass the array into vPortDefineHeapRegions(). */
vPortDefineHeapRegions( xHeapRegions );Initialising heap_5 after defining the memory blocks to be used by the heap

        xPortGetFreeHeapSize() API 函数被调用时返回未分配的堆空间总量, xPortGetMinimumEverFreeHeapSize() API 函数返回 FreeRTOS 应用程序启动的系统中已存在的最小空闲堆空间量。 这两个函数都没有提供关于未分配的 内存如何碎片化为小块的信息。

        pvPortCalloc() 函数的签名与标准库 calloc 函数相同。它为一个对象数组分配内存, 并将分配的存储空间中的所有字节初始化为零。如果分配成功, 它会返回指向分配的内存块中最低字节的指针。如果分配失败,它会返回一个空指针。

        vPortGetHeapStats() API 函数提供了有关堆状态的其他信息。

Heap相关的函数

pvPortMalloc/vPortFree

函数原型

void * pvPortMalloc( size_t xWantedSize ); // 分配内存,如果分配内存不成功,则返回值为NULL。void vPortFree( void * pv );  // 释放内存

        作用:分配内存、释放内存。如果分配内存不成功,则返回值为NULL。

xPortGetFreeHeapSize

函数原型

size_t xPortGetFreeHeapSize( void );

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

        注意:在heap_3中无法使用。

xPortGetMinimumEverFreeHeapSize

函数原型

size_t xPortGetMinimumEverFreeHeapSize( void );

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

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

malloc失败的钩子函数

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

        在pvPortMalloc函数内部,所以,如果想使用这个钩子函数:在FreeRTOSConfig.h中,把configUSE_MALLOC_FAILED_HOOK定义为1。提供vApplicationMallocFailedHook函数

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

常用的管理方法

动态内存分配(Dynamic Memory Allocation)

        FreeRTOS提供了用于动态内存分配的内置函数,如pvPortMalloc和vPortFree。这些函数允许任务在运行时请求和释放内存。这种方法对于需要灵活管理内存的应用非常有用,但需要小心避免内存泄漏和碎片化。

静态内存分配(Static Memory Allocation)

        FreeRTOS允许用户在编译时为任务和内核对象(如队列、信号量等)分配静态内存。通过在FreeRTOS配置文件中定义合适的宏,可以将任务的堆栈和内核对象的内存静态分配。

内存池(Memory Pools)

        内存池是在系统初始化时创建的一块内存区域,用于存储固定大小的内存块。任务可以从内存池中申请内存块,并在使用完毕后将其返回给内存池。这有助于减少内存碎片化。

堆栈自动增长(Stack Auto-Grow)

        FreeRTOS允许任务的堆栈在运行时自动增长,以适应任务运行期间的动态需求。这种方法使得在任务执行时不需要预先知道堆栈的最大深度,但也需要谨慎防止堆栈溢出。

静态API和动态API的混合使用

        在FreeRTOS中,可以选择性地使用静态API或动态API,根据应用需求选择适当的方式。这样可以在一些任务中使用静态分配,而在另一些任务中使用动态分配,以充分发挥各种方法的优势。

        FreeRTOS提供了灵活的内存管理机制,可以根据具体的应用场景选择合适的方法。使用动态内存分配时需要特别注意内存泄漏和碎片化的问题,而静态内存分配和内存池则可以在一定程度上减少这些问题。

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

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

相关文章

Leetcode—2646.最小化旅行的价格总和【困难】

2023每日刷题&#xff08;五十三&#xff09; Leetcode—2646.最小化旅行的价格总和 算法思想 看灵神的 实现代码 class Solution { public:int minimumTotalPrice(int n, vector<vector<int>>& edges, vector<int>& price, vector<vector&l…

发现数学之美--微积分的起源和用途(一文搞懂微积分)

数学&#xff0c;改变世界的基石。微积分十九世纪的三大自然发现之一&#xff0c;迪卡尔建立了解析几何&#xff0c;把数与图结合在一起&#xff0c;微积分的发现与创立&#xff0c;是数学新的里程碑&#xff0c;解决了常规方法无法解决的问题&#xff0c;是一次伟大的革命。迪…

git安装和配置

git安装和配置 一、软件介绍 Git是一个免费开源的分布式版本控制系统&#xff0c;旨在快速高效地处理从小型到大型项目的所有内容。 Git易于学习&#xff0c;占地面积小&#xff0c;性能闪电般快。它以廉价的本地分支、方便的暂存区域和多个工作流等功能胜过了Subversion、C…

【论文阅读】Reachability and distance queries via 2-hop labels

Cohen E, Halperin E, Kaplan H, et al. Reachability and distance queries via 2-hop labels[J]. SIAM Journal on Computing, 2003, 32(5): 1338-1355. Abstract 图中的可达性和距离查询是许多应用的基础&#xff0c;从地理导航系统到互联网路由。其中一些应用程序涉及到巨…

金南瓜SECS/GEM C# SDK 快速使用指南

本文对如何使用金南瓜SECS/GEM C# SDK 快速创建一个满足SECS/GEM通信要求的应用程序&#xff0c;只需简单3步完成。 第一步&#xff1a;创建C# .NET程序 示例使用Visual Studio 2010&#xff0c;使用者可以选择更高级版本 Visual Studio 第二步&#xff1a;添加DLL库引用&am…

图论-并查集

并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图,求最小生成树Kruskal算法和最近公共祖先(LCA)等. 并查集的基本操作主要有: .1.初始化 2.查询find 3.合并union 一般我们都会采用路径压缩 这样…

git标签的管理与思考

git 标签管理 git 如何打标签呢&#xff1f; 标签是什么? 标签 相当于一个 版本管理的一个贴纸&#xff0c;随时 可以通过标签 切换到 这个版本的状态 &#xff0c; 有人可能有疑问 git commit 就可以知道 代码的改动了&#xff0c; 为啥还需要标签来管理呢&#xff1f; …

node笔记

文章目录 一、Node.js基础1. 认识Node.js01 nodejs的特性02 使用 Node.js 需要了解多少 JavaScript03 浏览器环境vs node环境 2. 开发环境搭建3. 模块、包、commonJS02 CommonJS规范03 modules模块化规范写法 4. Npm&Yarn01 npm的使用02 全局安装 nrm03 yarn使用 5. 内置模…

在idea中使用maven创建dynamic web project

1、先创建一个empty project 2、添加一个module , 核心是选择maven archetype webapp, 这个是maven提供的创建web工程的模版。 3、添加完等自动安装好即可 4、目录可能不完整 右键src---->点击New---->点击Directory &#xff08;注意&#xff1a;这是笔者所缺失的结…

每日一道c语言

任务描述 题目描述:输入10个互不相同的整数并保存在数组中&#xff0c;找到该最大元素并删除它&#xff0c;输出删除后的数组 相关知识&#xff08;略&#xff09; 编程要求 请仔细阅读右侧代码&#xff0c;结合相关知识&#xff0c;在Begin-End区域内进行代码补充&#xf…

ooTD I 女儿是自己的,尽情打扮尽情可爱

分享女宝的时尚穿搭 奶乎乎的黄色也太好看了 超足充绒量&#xff0b;优质面料 柔软蓬松上身体验感超赞 怎么穿都好看系列 轻轻松松打造时尚造型&#xff01;&#xff01;

Linux 删除文件名乱码的文件

现象&#xff1a; 处理&#xff1a; 1.>ls -li 获取文件对应的ID号 2.把删除指定文件&#xff08;ID号 &#xff09;执行&#xff1a; find ./ -inum 268648910 -exec rm {} \;

微信小程序_介绍

开发准备 注册微信小程序 进入微信公众平台 点击立即注册&#xff0c;选择小程序&#xff0c;前往注册 完善个人/企业信息 获取AppID 进入小程序页面->开发->开发设置->AppID 下载微信开发者工具 微信官方下载下载微信开发者工具稳定版 创建项目 绑定AppID不使用…

用Rust刷LeetCode之27 移除元素

27. 移除元素 难度: 简单 原描述: 新描述: func removeElement(nums []int, val int) int { for i : 0; i < len(nums); i { if nums[i] val { nums append(nums[:i], nums[i1:]...) i-- } } return len(nums)} Rust 版本 下面这种写法编译无法通过: pub fn remove_…

基于ssm平面设计课程在线学习平台系统源码和论文

idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;平面设计课程在线学习平台系统也不例外&#xff0c;但目前国内的市场仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;…

【ArcGIS微课1000例】0079:ArcGIS Earth根据经纬坐标生成点shapefile

本文以气象台站数据的生成为例,详细介绍ArcGIS Earth中导入X、Y经纬度坐标,生成Shapefile点数据的流程。 文章目录 一、气象台站分布二、添加经纬度坐标三、符号化设置四、另存为一、气象台站分布 根据气象台站的经纬度坐标,可以很方便的在各种GIS平台上生成点,并保存为多…

智能优化算法应用:基于蜣螂算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜣螂算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜣螂算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜣螂算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

STM32基础教程 p16 窗口看门狗(WWDG)

1 窗口看门狗工作原理 1.1 简介 WWDG简介 窗口看门狗通常被用来监测&#xff0c;由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运 行序列而产生的软件故障。除非递减计数器的值在T6位变成0前被刷新&#xff0c;看门狗电路在达到预置 的时间周期时&#xff0c;会产…

联合基于信息论的安全和隐蔽通信的框架

这个标题很帅 abstractintroductionsystem modelPROPOSED JOINT OPTIMIZATION OF ITS AND COVERT TRANSMISSION RATE信息论安全 (ITS)隐蔽通信需要(CC)Joint Information-Theoretic Secrecy and Covert Communication in the Presence of an Untrusted User and Warden 202…

Shell数组函数:数组——数组和循环(四)

使用数组统计&#xff0c;用户shell的类型和数量 一、脚本编辑 [root192 ~]# vim shell.sh #!/bin/bash declare -A shells while read ii dotypeecho $ii | awk -F: {print $7}let shells[$type] done < /etc/passwdfor i in ${!shells[]} doecho "$i: ${shells[$i]…