打印
[经验分享]

FreeRTOS之动态内存管理(heap_1.c)详解

[复制链接]
655|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wakayi|  楼主 | 2024-11-7 14:08 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
简介
  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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

4084

帖子

1

粉丝