FreeRTOS内存管理详解:五种内存分配方法对比
FreeRTOS内存管理详解:五种内存分配方法对比
FreeRTOS的内存管理是系统开发中的重要环节,它提供了多种内存分配策略以适应不同的应用场景。本文将详细介绍FreeRTOS中五种不同的内存分配方法(heap_1.c至heap_5.c),帮助开发者更好地理解和使用这些功能。
FreeRTOS 内存管理简介
FreeRTOS在创建任务、队列、信号量等资源时,提供了两种内存申请方式:动态申请和静态分配。动态申请通过pvPortMalloc()函数从堆中分配内存,而静态分配则由用户预先定义所需的RAM空间。
不同的嵌入式系统对内存分配和时间要求各异,因此FreeRTOS将内存分配作为移植层的一部分,允许用户选择最适合的内存分配方法。当内核需要RAM时,可以使用pvPortMalloc()替代标准的malloc()函数,释放内存时则使用vPortFree()替代free()函数。这两种函数的原型与标准C库中的malloc()和free()类似。
FreeRTOS提供了五种内存分配方法,分别对应五个文件:heap_1.c、heap_2.c、heap_3.c、heap_4.c和heap_5.c。这些文件位于FreeRTOS的源码目录中。
内存碎片问题
在深入探讨各种内存分配方法之前,我们先了解一下什么是内存碎片。内存碎片是指在内存分配和释放过程中,由于分配和释放的内存块大小不一致,导致内存空间被分割成许多小块,这些小块可能无法被有效利用。
如上图所示,当一个应用释放了80B和10B的内存块后,如果另一个应用需要50B的内存,它可以使用剩余的未分配内存或已释放的80B内存块。然而,10B的内存块由于太小,除非有需要小于等于10B内存的应用,否则无法被使用,从而成为内存碎片。
经过多次分配和释放后,内存会被分割成大量小块,这些小块可能无法满足大多数应用的需求,最终导致实际可用内存减少,甚至导致应用程序因无法分配到合适内存而崩溃。FreeRTOS的heap_4.c提供了一种解决方案,即通过合并相邻的空闲内存块来提高内存利用率。
五种内存分配方法对比
内存管理方法 | 简介 | 优点 | 缺点 |
---|---|---|---|
heap_1.c | 简单静态分配方式,提供单一的内存堆,分配后内存块不会被释放,内存块大小在编译时确定。 | 实现简单,占用资源少,无内存碎片问题,对于资源分配固定的简单系统可靠性高。 | 缺乏灵活性,不能动态释放内存,不适用于需要频繁分配和释放内存的复杂场景。 |
heap_2.c | 基于单向链表管理固定大小的内存块,可动态分配和释放内存。 | 对于固定大小内存块的分配和释放操作相对简单高效,适用于内存块大小固定的频繁分配和释放场景,如相同大小任务栈的管理。 | 只能处理固定大小的内存块,存在内存碎片风险,当内存块大小需求不一致时,内存利用率可能较低。 |
heap_3.c | 对标准C库的malloc()和free()函数进行简单包装。 | 利用标准C库的功能,易于理解和移植,在熟悉标准C库内存管理的情况下可以快速上手。 | 依赖标准C库的性能和特性,可能存在标准C库本身的内存碎片问题,对一些资源受限的嵌入式系统可能不太适用。 |
heap_4.c | 采用双向链表管理可变大小的内存块,能合并相邻空闲内存块来提高利用率,可动态分配和释放。 | 能灵活处理不同大小的内存块分配,通过合并空闲内存块提高了内存利用率,适用于复杂多变的内存分配需求。 | 实现相对复杂,占用一定的系统资源用于管理内存链表,内存分配和释放操作可能比简单的方法耗时。 |
heap_5.c | 基于heap_4.c的算法扩展到多个不连续的内存区域,可在这些区域间分配和释放内存。 | 能够有效利用分散的内存资源,适用于内存分布不连续的系统,提高了整个系统的内存可用性。 | 管理多个区域的内存增加了复杂性,对内存管理的开销进一步增大,实现和调试难度较高。 |
字节对齐的目的
字节对齐是为了优化内存访问效率。现代处理器在访问内存时,通常要求数据的起始地址与数据的大小对齐。例如,一个32位的整数应该存储在4字节对齐的地址上。如果不满足对齐要求,处理器可能需要进行额外的操作来访问数据,这会降低性能。因此,内存分配时通常需要进行字节对齐处理。
heap_1内存分配方法
heap_1是最简单的内存分配方法,它从一个大数组(内存堆)中分配一小块内存。内存堆在heap_x.c文件中定义,大小为configTOTAL_HEAP_SIZE。代码实现和内存分配过程都非常简单,适合那些不需要动态内存分配的应用。
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
//需要用户自行定义内存堆
//当宏 configAPPLICATION_ALLOCATED_HEAP 为 1 的时候需要
//用户自行定义内存堆,否则的话由编译器来决定,默认都是由编译器
//来决定的。如果自己定义的话就可以将内存堆定义
//到外部 SRAM 或者 SDRAM 中
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //编译器决定
#endif
void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;
static uint8_t *pucAlignedHeap = NULL;
//确保字节对齐
#if( portBYTE_ALIGNMENT != 1 )
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
//需要进行字节对齐
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize &
portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
vTaskSuspendAll();
{
if( pucAlignedHeap == NULL )
{
//确保内存堆的开始地址是字节对齐的
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE )&
ucHeap[ portBYTE_ALIGNMENT ] ) &
( ~( ( portPOINTER_SIZE_TYPE )
portBYTE_ALIGNMENT_MASK ) ) );
}
//检查是否有足够的内存供分配,有的话就分配内存
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )
{
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;
}
关键概念解释
portBYTE_ALIGNMENT
:指定字节对齐的规则,即数据存储时按照多少字节的边界进行对齐。例如,如果portBYTE_ALIGNMENT
被定义为4,意味着数据存储时会按照4字节的边界进行对齐。portBYTE_ALIGNMENT_MASK
:与portBYTE_ALIGNMENT
相关的掩码值。通常,portBYTE_ALIGNMENT
是2的幂次方,例如2、4、8等。当portBYTE_ALIGNMENT
是2的幂次方时,portBYTE_ALIGNMENT_MASK
的值为portBYTE_ALIGNMENT - 1
。例如,如果portBYTE_ALIGNMENT
为4(二进制100),那么portBYTE_ALIGNMENT_MASK
为3(二进制011)。
heap_2内存分配方法
heap_2提供了一个更好的分配算法,与heap_1不同的是,heap_2提供了内存释放函数。但是,heap_2不会将释放的内存块合并成一个大块,这会导致内存碎片问题。随着不断的内存分配和释放,内存堆会被分割成许多大小不一的内存块。
heap_3内存分配方法
heap_3是对标准C库中的malloc()和free()函数的简单封装。FreeRTOS对这两个函数进行了线程保护,使其在多任务环境中安全使用。
heap_4内存分配方法
heap_4提供了最优的匹配算法,与heap_2不同的是,heap_4会将内存碎片合并成一个大的可用内存块。它采用双向链表结构来管理内存,并能够合并相邻的空闲内存块,从而提高内存利用率。当一个任务结束并释放其占用的内存块后,heap_4可以将该内存块与相邻的空闲内存块合并为一个更大的空闲内存块,以便后续分配给需要较大内存空间的其他任务或资源。
heap_5内存分配方法
heap_5是在heap_4的基础上实现的,增加了管理多个非连续内存区域的能力。heap_5默认没有定义内存堆,需要用户手动指定内存区域的信息并进行初始化。