在C语言的库函数中,有malloc(),free()等函数,但是在Freertos中,它们不适用。
不适用于在资源紧缺的嵌入式系统中
这些函数的实现过于复杂,占据的代码空间太大
并非线程安全的(thread-safe)
运行有不确定性
内存碎片化
使用不同的编译器时,需要进行复杂的配置
有时难以调试
FreeRTOS的五种内存管理方法:
文件存储在01_freertos_template\Middlewares\Third_Party\FreeRTOS\Source\portable\MemMang中,通过不同的应用场景调用合适的管理方式,从而更优的解决问题。
<img src="…/some_
文件名 | 优点 | 缺点 |
---|---|---|
heap_1.c | 分配简单,时间确定、不产生碎片 | 只分配、不回收 |
heap_2.c | 动态分配、最佳匹配算法 | 产生碎片、时间不定 |
heap_3.c | 调用标准库函数、为标准库的malloc()、free()函数提供线程保护 | 不确定性速度慢、 占用更多代码空间 |
heap_4.c | 相邻空闲内存空间被合并、可解决内存碎片化问题、 | 时间不定、可能消耗更多的处理时间 |
heap_5.c | 在heap_4的基础上增加了管理多个连续内存区域的能力 | 时间不定、需要手动指定内存区域的信息并初始化、增加了配置的复杂性 |
根据表格内容,如何选择合适的内存管理策略,应考虑以下因素:
确定性:如果应用对响应时间和执行时间有严格要求,应选择确定性强的内存管理策略,如heap_1或heap_4.
动态内存分配:对于需要频繁创建和删除任务或对象的系统,如heap_2或heap_4更合适。
资源受限环境:在代码空间和内存资源受限的系统中,应该避免使用heap_3,可以选择简单的heap_1或高效的heap_4/heap_5。
内存连续性:如果系统的内存管理分散在多个非连续的区域,heap_5提供了额外的优势,能够更好地管理和利用这些内存资源。
各个文件详解:
heap1.c
这是所有实现最简单的一个。如果程序中不需要删除内核对象,那么可以使用heap_1,它没有碎片问题,它只实现了pvPortMalloc,没有实现vPortFree。一些要求非常严格的系统里,不允许使用动态内存,就可以使用heap_1。
当需要分配RAM时,这个内存分配方案 只是简单的将一个大数组细分出一个字集来。
* A few bytes might be lost to byte aligning the heap start address. */
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
/* Allocate the memory for the heap. */
/* 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调用时,从这个数组中分配空间。
heap_1功能介绍:
用于从不会删除任务、队列、信号量、互斥量等的应用程序。
执行时间是确定的并且不会产生内存碎片。
实现和分配过程是非常简单的,需要的内存是从一个静态数组中分配的,意味着这种内存分配通常只是适用于那些不进行动态内存分配的应用。
heap_2.c
heap_2也是在数组上分配内存,跟heap_1不一样的地方在于heap_2使用最佳匹配算法来分配内存,它支持vPortFree;他不会把相邻的空闲块合成一个更大的块,这就导致了内存碎片。
使用场景:频繁地创建、删除任务,但是任务的栈大小都是相同的(创建任务时,需要分配TCB和栈,TCB总是一样的)。
heap_2.c适用于需要动态创建任务的大多数小型实时系统(smallreal time)。
heap_3.c
简单的包装了标准库中的malloc()和free()函数,包装后的malloc()和free()函数具备线程保护。
C库里的malloc、free函数并非线程安全的,Heap_3中先暂停FreeRTOS的调度器,再去调用这些函数,使用这种方法实现了线程安全。
heap_3具有不确定性,可能明显的增大RTOS内核的代码大小。
heap_4.c
heap_4也是使用大数组来分配内存,Heap_4使用首次适应算法来分配内存,它还会把相邻的空闲内存合并为一个更大的空闲内存,这有助于较少内存的碎片问题。
功能:可以用于重复分配、删除任务、队列、信号量、互斥量等等的应用程序。可用于分配和释放随机自己内存的情况,并不像heap_2.c那样产生严重碎片。不具有确定性,但是比标准库中的malloc()函数具有更高的效率。
heap_4.c还特别适用于移植层代码,可以直接使用pvPortMalloc()和 vPortFree()函数来分配和释放内存。
heap_5.c
heap_5分配内存、释放内存的算法跟heap_4一样。允许堆栈跨多个非连续的内存区。
既然内存时分隔开的,那么就需要进行初始化:确定这些内存块在哪、多大:
- 在使用 pvPortMalloc 之前,必须先指定内存块的信息
- 使用 vPortDefineHeapRegions 来指定这些信息
vPortDefineHeapRegions()函数只需要单个参数。该参数是一个HeapRegion_t结构体类型数组。HeapRegion_t在portable.h中定义,如下所示:
typedef struct HeapRegion
{
/* 用于内存堆的内存块起始地址*/
uint8_t *pucStartAddress;
/* 内存块大小 */
size_t xSizeInBytes;
} HeapRegion_t;
综上所述,选择合适的内存管理策略不仅需要考虑系统的实时性和安全性,还要考虑系统的动态内存需求和资源限制情况。合理选择和配置内存管理方案,可以显著提高系统的性能和稳定性。在实际应用中,还应根据具体情况调整和优化内存管理策略,以满足特定的应用需求和提高系统的整体性能。