FreeRTOS Heap Memory Management (2) - heap1源码分析

FreeRTOS Heap Memory Management (2) - heap1源码分析

/* FreeRTOS Kernel V10.4.1 */

原文链接:https://www.cnblogs.com/yanpio/p/14817756.html

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 的首地址不确定,所以出于对齐的考虑,最多可能浪费 8Bytes用来对齐,因此实际可用的BytesconfigADJUSTED_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 = 8xWantedSize = 13,则实际分配的大小会调整为16 Bytes。当然,如果portBYTE_ALIGNMENT = 1,则不需要调整。

portBYTE_ALIGNMENT_MASKportBYTE_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 );
}
/*-----------------------------------------------------------*/
posted on 2021-05-27 14:29  Yanpeng0527  阅读(208)  评论(1编辑  收藏  举报