FreeRTOS提供5种堆内存管理方案(heap_1.c至heap_5.c),每种方案针对不同应用场景设计,源码位于FreeRTOS/Source/portable/MemMang
目录。
标准 C 库 malloc() 和 free() 函数分配堆空间有以下缺点:
FreeRTOS 将内存分配 API 保留在其可移植层,提供了五种内存管理算法:
关键配置参数
configTOTAL_HEAP_SIZE:定义堆总大小 configAPPLICATION_ALLOCATED_HEAP:允许用户自定义堆位置 configHEAP_CLEAR_MEMORY_ON_FREE:释放时清零内存
实际选择需考虑应用场景的内存分配模式、实时性要求和硬件资源限制。
最简单的内存分配方案,仅支持pvPortMalloc()不支持vPortFree()。适用于创建任务后不再删除任务的场景。 内存分配采用静态数组形式:
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //默认大小17k
pvPortMalloc( size_t xWantedSize )
/*-----------------------------------------------------------*/
void * pvPortMalloc( size_t xWantedSize )
{
void * pvReturn = NULL;
static uint8_t * pucAlignedHeap = NULL;
/**************** portBYTE_ALIGNMENT 默认等于8,下面部分就是做字节对齐的 ******************/
#if ( portBYTE_ALIGNMENT != 1 ) //如果portBYTE_ALIGNMENT !=1,需要对齐操作
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK ) //查看申请的内存大小是否是8的倍数
{
if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) ) > xWantedSize )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
else
{
xWantedSize = 0;
}
}
}
#endif /* if ( portBYTE_ALIGNMENT != 1 ) */
/**************************************************************************************/
/************** 检查空堆空间是否分配成功,对齐,更新xNextFreeByte,指向未分配的空间 **********/
vTaskSuspendAll();
{
if( pucAlignedHeap == NULL )
{
/* Ensure the heap starts on a correctly aligned boundary. */
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT - 1 ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
/* Check there is enough room left for the allocation and. */
if( ( xWantedSize > 0 ) && /* valid size */
( ( 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 )
{
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
/*-----------------------------------------------------------*/
void vPortInitialiseBlocks( void )
{
/* Only required when static memory is not cleared. */
xNextFreeByte = ( size_t ) 0;
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void )
{
return( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}
支持内存分配与释放,使用最佳匹配算法但会产生碎片。采用链表结构管理空闲块:
typedef struct A_BLOCK_LINK
{
struct A_BLOCK_LINK * pxNextFreeBlock; /*指向块*/
size_t xBlockSize; /*块大小*/
} BlockLink_t;
static void prvHeapInit( void )
pvPortMalloc( size_t xWantedSize ):
void * pvPortMalloc( size_t xWantedSize )
{
BlockLink_t * pxBlock;
BlockLink_t * pxPreviousBlock;
BlockLink_t * pxNewBlockLink;
/***** xHeapHasBeenInitialised用于判断是否是第一次申请堆空间 *****/
PRIVILEGED_DATA static BaseType_t xHeapHasBeenInitialised = pdFALSE;
void * pvReturn = NULL;
size_t xAdditionalRequiredSize;
vTaskSuspendAll(); //关中断
{
if( xHeapHasBeenInitialised == pdFALSE )
{
prvHeapInit(); //初始化堆空间
xHeapHasBeenInitialised = pdTRUE;
}
if( xWantedSize > 0 )
{
//xWantedSize 向上对齐
xAdditionalRequiredSize = heapSTRUCT_SIZE + portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK );
//溢出检查
if( heapADD_WILL_OVERFLOW( xWantedSize, xAdditionalRequiredSize ) == 0 )
{
xWantedSize += xAdditionalRequiredSize; //如果不会溢出,调整wantedSize大小
}
else
{
xWantedSize = 0;
}
}
/******************************
如果xWantedSize合法,遍历链表依据xBlockSize 确定链表插入位置
******************************/
if( heapBLOCK_SIZE_IS_VALID( xWantedSize ) != 0 )
{
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
{
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if( pxBlock != &xEnd ) //说明有足够的空间
{
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
/* This block is to be split into two. Create a new block
* following the number of bytes requested. The void cast is
* used to prevent byte alignment warnings from the compiler. */
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
/* Calculate the sizes of two blocks split from the single
* block. */
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
pxBlock->xBlockSize = xWantedSize;
/* Insert the new block into the list of free blocks. */
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
xFreeBytesRemaining -= pxBlock->xBlockSize;
/* The block is being returned - it is allocated and owned
* by the application and has no "next" block. */
heapALLOCATE_BLOCK( pxBlock );
pxBlock->pxNextFreeBlock = NULL;
}
}
}
traceMALLOC( pvReturn, xWantedSize );
}
( void ) xTaskResumeAll();
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
/*-----------------------------------------------------------*/
对标准库malloc/free进行线程安全封装:
void *pvPortMalloc( size_t xWantedSize ) {
vTaskSuspendAll();
pvReturn = malloc( xWantedSize );
xTaskResumeAll();
}
通过挂起调度器保证原子操作。
合并相邻空闲块减少碎片,采用首次适应算法:
if( ( puc + xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock ) {
xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
pxIterator->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}