简介
FreeRTOS 支持 5 种动态内存管理方案,分别通过文件 heap_1.c,heap_2.c,heap_3.c,heap_4.c 和 heap_5.c 实现。这 5 个文件在 FreeRTOS 源码包中的路径是:FreeRTOS\Source\portable\MemMang。具体如下图:
用户在自己的项目中如果要使用 FreeRTOS ,则必须从中以上5中内存方案中选择一种 。5种方案各有各的优势,分别适用于不同的应用场景。
再具体的实现上,FreeRTOS 内核规定的几个内存管理函数原型。系统内部及用户如果要使用内存,只能通过该函数接口进行申请。因此完全可以有用户自己实现。具体函数接口如下(不同方案稍有区别):
void *pvPortMalloc( size_t xSize ) :内存申请函数
void vPortFree( void *pv ) :内存释放函数
void vPortInitialiseBlocks( void ) :初始化内存堆函数
size_t xPortGetFreeHeapSize( void ):获取当前未分配的内存堆大小
size_t xPortGetMinimumEverFreeHeapSize( void ):获取未分配的内存堆历史最小值
heap_1.c
这种内存分配方式最简单直接,速度快程序简单。适用于分配完内存后不需要回收的场合。只允许管理一个静态的数组ucHeap,内存从静态Ram中由系统分配,不能指定管理外部SRAM,或者管理堆中的内存。下面就结合源码,详细来介绍一下heap_1.c。
配置
在 FreeRTOS 的配置文件(FreeRTOSConfig.h)中,关于内存管理部分的配置项主要有以下几个,要使用FreeRTOS 提供的内存实现策略,则必须进行有效的配置:
configSUPPORT_STATIC_ALLOCATION :
设置为1,那么可以使用应用程序编写器提供的RAM创建RTOS对象。设置为0,则只能使用从FreeRTOS堆分配的RAM创建RTOS对象。默认(未定义时)为0
configSUPPORT_DYNAMIC_ALLOCATION :
设置为1,则可以使用从FreeRTOS堆中自动分配的RAM创建RTOS对象。设置为0,则只能使用应用程序编写器提供的RAM创建RTOS对象。默认(未定义)为1
configTOTAL_HEAP_SIZE :
FreeRTOS堆中可用的RAM总量。该值仅在configSUPPORT_DYNAMIC_ALLOCATION设置为 1 且应用程序使用FreeRTOS源代码下载中提供的示例内存分配方案之一时该定义才有效。 有关详细信息,请参阅内存配置部分。
configAPPLICATION_ALLOCATED_HEAP :
默认情况下,FreeRTOS堆由FreeRTOS声明,并由链接器放置在内存中。 将configAPPLICATION_ALLOCATED_HEAP设置为1允许应用程序编写器声明堆,这允许应用程序将堆放置在内存中的任何位置。
如果使用heap_1.c,heap_2.c或heap_4.c,并且configAPPLICATION_ALLOCATED_HEAP设置为1,那么应用程序编写器必须提供一个具有完全名称和维度的uint8_t数组,如下所示:uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
该数组将用作FreeRTOS堆。 如何将数组放置在特定的内存位置取决于使用的编译器 - 请参阅您的编译器文档。
从上面的配置不难看出,FreeRTOS给与了用户极大的权限(配置灵活性),甚至允许完全由用户自己实现内存堆的管理。例如,使用使用静态分配方式(版本9.0.0之后)。
heap_1.c是这是 5 种内存管理方案中最简单的一个,简单到只能申请内存(没有释放)。heap_1.c方案中,内存堆实际上是一个很大的数组,名为:static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];。源码如下:
/* heap_1.h的内存管理方式,只允许管理一个静态的数组ucHeap,内存从静态Ram中由系统分配 */
#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
/* 首地址按 portBYTE_ALIGNMENT 对齐后内存容量的大小 */
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
/* Allocate the memory for the heap. */
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
/* 用户自定义静态内存的位置,名称必须为ucHeap. */
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
/* 如果 没有启用用户自定义,则使用默认的 */
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */
/* Index into the ucHeap array. 记录已经分配的内存大小(主要用来定位下一个空闲的内存堆位置) */
static size_t xNextFreeByte = ( size_t ) 0;
具体的说明,见上面的注释即可。
内存对齐
在 portmacro.h (Source/Portable/ + 对应编译器 + 平台 目录下) 的常量 portBYTE_ALIGNMENT 定义了字节对齐,对应的这个变量决定了portable.h 中的一个常量 portBYTE_ALIGNMENT_MASK, 对应关系如下:
至于为什么会考虑内存对齐呢?这个在LwIP的博文中有详细的说明,再次不在赘述!
void *pvPortMalloc( size_t xWantedSize )
这个函数是由 FreeRTOS 规定的内存分配函数。系统内部及用户如果要使用内存,只能通过该函数接口进行申请。
void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;
/* 按 portBYTE_ALIGNMENT 对齐后内存首地址。!!!注意这是个静态变量,在第一次使用时会被初始化!!! */
static uint8_t *pucAlignedHeap = NULL;
/* 确保指定的静态内存块是 按 portBYTE_ALIGNMENT 对齐的(如果指定的对齐为1字节,就没必要处理了). */
#if( portBYTE_ALIGNMENT != 1 )
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK ) /* 其实就是xWantedSize % portBYTE_ALIGNMENT */
{
/* Byte alignment required.将 xWantedSize 按照 portBYTE_ALIGNMENT 强制对齐 */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
vTaskSuspendAll(); /* 挂起所有任务,防止重入 */
{
if( pucAlignedHeap == NULL ) /* 静态变量,仅在第一次初始化 */
{
/* Ensure the heap starts on a correctly aligned boundary. 这个实现对齐的方式可以参考,很溜! */
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
/* Check there is enough room left for the allocation. 检查剩余空间是否足够,且 没有溢出 */
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */
{
/* Return the next free byte then increment the index past this
block. */
pvReturn = pucAlignedHeap + xNextFreeByte;
xNextFreeByte += xWantedSize;
}
traceMALLOC( pvReturn, xWantedSize );
}
( void ) xTaskResumeAll(); /* 恢复任务 */
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
/* 如果分配内存失败,调用回调函数(如果开启了钩子函数) */
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
void vPortFree( void *pv )
这个函数是由 FreeRTOS 规定的内存释放函数。系统内部及用户如果要释放内存,只能通过该函数接口进行申请。有下面的实现可知,在heap_1.c方案,内存一旦申请便无法释放!
void vPortFree( void *pv )
{
/* Memory cannot be freed using this scheme. See heap_2.c, heap_3.c and
heap_4.c for alternative implementations, and the memory management pages of
http://www.FreeRTOS.org for more information. */
( void ) pv;
/* Force an assert as it is invalid to call this function. */
configASSERT( pv == NULL );
}
void vPortInitialiseBlocks( void )
这个函数是由 FreeRTOS 规定的内存管理初始化函数。
void vPortInitialiseBlocks( void )
{
/* Only required when static memory is not cleared. */
xNextFreeByte = ( size_t ) 0;
}
size_t xPortGetFreeHeapSize( void )
这个函数是由 FreeRTOS 规定的获取动态内存的剩余大小的函数。唯一需要注意的就是,其大小是在对齐之后的!
size_t xPortGetFreeHeapSize( void )
{
return ( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/ZCShouCSDN/article/details/84928881
|