FreeRTOS Heap Memory Management (2) - heap1源码分析
/* FreeRTOS Kernel V10.4.1 */
1 变量定义
#include <stdlib.h>
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#include "FreeRTOS.h"
#include "task.h"
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
/* 见注释(1) */
/* A few bytes might be lost to byte aligning the heap start address. */
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
/* 见注释(2) */
/* 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 ];//32k
#endif /* configAPPLICATION_ALLOCATED_HEAP */
/* Index into the ucHeap array. */
static size_t xNextFreeByte = ( size_t ) 0;
(1)configADJUSTED_HEAP_SIZE
注释
portBYTE_ALIGNMENT
定义了内存对齐的字节数,由具体的移植实现而定。在portmacro.h
中定义,可以为 1, 2, 4, 8, 16, 32
。
假设 portBYTE_ALIGNMENT = 8
。由于数组ucHeap
的首地址不确定,所以出于对齐的考虑,最多可能浪费 8
个Bytes
用来对齐,因此实际可用的Bytes
(configADJUSTED_HEAP_SIZE
)可能达不到设置的理论值(configTOTAL_HEAP_SIZE
)。最差的情况就是如下所示。当然,有可能对齐用不到 8 Bytes
,但是此处就忽略这微不足道的几个Bytes
了。
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
(2) ucHeap
注释
ucHeap
就是 FreeRTOS Heap Memory Management (1) - 内存分配介绍 中提到的FreeRTOS heap
,即预先分配好的数组。如果宏configAPPLICATION_ALLOCATED_HEAP
设置为1,则在应用中来分配数组空间(可以指定具体的内存地址),否则在此处进行分配。
2 pvPortMalloc()
/* 函数整体思路如下:
* 1.检查所需分配的内存大小是否对齐
* 2.如果是首次分配,则对起始地址进行对齐操作,否则继续执行
* 3.检查数组越界等情况
* 4.判断是否分配成功,如果失败是否调用钩子函数
*/
void * pvPortMalloc( size_t xWantedSize )
{
void * pvReturn = NULL;
/* static 变量,只被初始化一次,仅在首次分配时,需要将数组首地址进行分配。
* 从第2次分配开始,数组首地址已经对齐,且每次分配的大小也经过对齐,所以不需要再对地址进行对齐*/
static uint8_t * pucAlignedHeap = NULL;
/* 见注释(1)*/
#if ( portBYTE_ALIGNMENT != 1 )
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
vTaskSuspendAll(); /* 挂起所有任务,防止函数重入 */
{
// pucAlignedHeap 是static变量,只初始化一次,第一次分配后,此条件就进不来了
if( pucAlignedHeap == NULL )
{
/* 见注释(2)*/
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && /* 数组是否还有足够空间进行分配 */
( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) ) /* 分配数量 xWantedSize 是否大于 0. */
{
/* 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;
}
(1)xWantedSize
注释
出于内存对齐的考虑,需要对申请的大小 xWantedSize
进行对齐。假设 portBYTE_ALIGNMENT = 8
, xWantedSize = 13
,则实际分配的大小会调整为16 Bytes
。当然,如果portBYTE_ALIGNMENT = 1
,则不需要调整。
portBYTE_ALIGNMENT_MASK
和portBYTE_ALIGNMENT
的关系如下,即**portBYTE_ALIGNMENT_MASK = portBYTE_ALIGNMENT - 1
**
#if portBYTE_ALIGNMENT == 32
#define portBYTE_ALIGNMENT_MASK ( 0x001f )
#endif
#if portBYTE_ALIGNMENT == 16
#define portBYTE_ALIGNMENT_MASK ( 0x000f )
#endif
#if portBYTE_ALIGNMENT == 8
#define portBYTE_ALIGNMENT_MASK ( 0x0007 )
#endif
#if portBYTE_ALIGNMENT == 4
#define portBYTE_ALIGNMENT_MASK ( 0x0003 )
#endif
#if portBYTE_ALIGNMENT == 2
#define portBYTE_ALIGNMENT_MASK ( 0x0001 )
#endif
#if portBYTE_ALIGNMENT == 1
#define portBYTE_ALIGNMENT_MASK ( 0x0000 )
#endif
/* 判断能否对齐, 其实就是xWantedSize % portBYTE_ALIGNMENT */
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
/* portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) 可以得到距离对齐还差几个字节 */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
(2)pucAlignedHeap
注释
/* portPOINTER_SIZE_TYPE 是指针类型数据长度,在32位应用中,定义为 uint32_t
* 假设 portBYTE_ALIGNMENT = 8,则portBYTE_ALIGNMENT_MASK = 0x0007*/
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
/* 上述语句展开如下 */
pucAlignedHeap = ( uint8_t * ) ( ( ( uint32_t ) & ucHeap[ 8 ] ) & ( ~( ( uint32_t ) 0x0007 ) ) );
/* 将数据类型转换去掉,如下 */
pucAlignedHeap = (& ucHeap[ 8 ]) & ( ~ 0x0007 ) ;
/* 例如: ucHeap = 0x40000003, 则( 0x40000003 + 8 ) & 0xFFFFFFF7 = 0x40000008,
* 即pucAlignedHeap = 0x40000008 */
3 其他函数
void vPortFree( void * pv )
{
( void ) pv;
/* 如果调用vPortFree(),则会卡在此处. */
configASSERT( pv == NULL );
}
/*-----------------------------------------------------------*/
void vPortInitialiseBlocks( void )
{
/* Only required when static memory is not cleared. */
xNextFreeByte = ( size_t ) 0;
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void )
{
return( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}
/*-----------------------------------------------------------*/