FreeRTOS--堆内存管理(二)

本文详细剖析了FreeRTOS中五个不同的内存管理函数实现,包括heap_1的内存申请与释放,heap_2的内存块管理,以及heap_3、heap_4和heap_5的内存堆初始化、内存块插入和内存分配。通过代码实例揭示了内存对齐、内存分配策略和内存碎片处理技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


上一篇文章说了FreeRTOS实现堆内存的原理,这一篇文章说一下常用函数的代码的具体实现。

heap_1

heap_1的内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE

内存申请函数

heap_1的内存申请函数pvPortMalloc()源码如下:

/*-----------------------------------------------------------*/

void * pvPortMalloc( size_t xWantedSize )
{
    void * pvReturn = NULL;
    static uint8_t * pucAlignedHeap = NULL;

    /* Ensure that blocks are always aligned. */
    #if ( portBYTE_ALIGNMENT != 1 )
        {
            if( xWantedSize & portBYTE_ALIGNMENT_MASK )
            {
                /* Byte alignment required. Check for overflow. */
                if ( (xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) )) > xWantedSize )
                {
                    xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
                }
                else
                {
                    xWantedSize = 0;
                }
            }
        }
    #endif

    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 )
            {
                extern void vApplicationMallocFailedHook( void );
                vApplicationMallocFailedHook();
            }
        }
    #endif

    return pvReturn;
}
/*-----------------------------------------------------------*/

下面的代码块是判断内存块是否对齐

void * pvPortMalloc( size_t xWantedSize )
{
    void * pvReturn = NULL;
    static uint8_t * pucAlignedHeap = NULL;

    /* Ensure that blocks are always aligned. */
    #if ( portBYTE_ALIGNMENT != 1 )
        {
            if( xWantedSize & portBYTE_ALIGNMENT_MASK ) 
                /****
                * 令  length = portBYTE_ALIGNMENT_MASK二进制中1的个数
                *       x = xWantedSize的后length位的值
                *       y = portBYTE_ALIGNMENT_MASK-x+1
                *   如果 xWantedSize & portBYTE_ALIGNMENT_MASK = 0,则xWantedSize肯定是(portBYTE_ALIGNMENT_MASK+1)的整数倍
                *   如果  xWantedSize & portBYTE_ALIGNMENT_MASK不为0,则xWantedSize的后length位肯定有1,xWantedSize不是portBYTE_ALIGNMENT_MASK的整数倍
                *   如果 xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) )大于xWantedSize,说明后length位肯定有1
                *   xWantedSize = xWantedSize + y,xWantedSize变成portBYTE_ALIGNMENT整数倍
                ******/
            {
                /* Byte alignment required. Check for overflow. */
                if ( (xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) )) > xWantedSize )
                {
                    xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
                }
                else
                {
                    xWantedSize = 0;
                }
            }
        }
    #endif

#if ( portBYTE_ALIGNMENT != 1 )是判断是否进行字节对齐,portBYTE_ALIGNMENT默认为8:
在这里插入图片描述

#if portBYTE_ALIGNMENT == 32
    #define portBYTE_ALIGNMENT_MASK    ( 0x001f )
#elif portBYTE_ALIGNMENT == 16
    #define portBYTE_ALIGNMENT_MASK    ( 0x000f )
#elif portBYTE_ALIGNMENT == 8
    #define portBYTE_ALIGNMENT_MASK    ( 0x0007 )
#elif portBYTE_ALIGNMENT == 4
    #define portBYTE_ALIGNMENT_MASK    ( 0x0003 )
#elif portBYTE_ALIGNMENT == 2
    #define portBYTE_ALIGNMENT_MASK    ( 0x0001 )
#elif portBYTE_ALIGNMENT == 1
    #define portBYTE_ALIGNMENT_MASK    ( 0x0000 )
#else /* if portBYTE_ALIGNMENT == 32 */
    #error "Invalid portBYTE_ALIGNMENT definition"

因为portBYTE_ALIGNMENT 定义为8,所以portBYTE_ALIGNMENT_MASK 定义为0x0007,xWantedSize 是无符号整数类型(unsigned int),即32位,xWantedSize 与宏portBYTE_ALIGNMENT_MASK 进行与运算来判断xWantedSize是否为portBYTE_ALIGNMENT 的整数倍,如果等于0就说明xWantedSizr是portBYTE_ALIGNMENT 的整数倍,否则的话将xWantedSize加上一个值,让xWantedSize 变成portBYTE_ALIGNMENT 的整数倍。具体可以看一下代码,我加了注释。
调用vTaskSuspendAll()挂起任务调度器,因为申请内存过程中要做保护,不能被其他任务打断。

pucAlignedHeap是一个静态变量,下面这个代码是初试化pucAlignedHeap,pucAlignedHeap指向ucHeap[ portBYTE_ALIGNMENT - 1 ]

if( pucAlignedHeap == NULL )
        {
            /* Ensure the heap starts on a correctly aligned boundary. */
            /***
            * 初始化pucAlignedHeap,pucAlignedHeap指向ucHeap[ portBYTE_ALIGNMENT - 1 ]
            **/
            pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT - 1 ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
        }

接下来就是分配内存了,最后返回pvReturn,是分配内存的首地址,其中xNextFreeByte是静态变量,表示堆ucHeap已经用了多少内存,configADJUSTED_HEAP_SIZE= configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT,是可以用的内存容量。还有如果剩余内存小于portBYTE_ALIGNMENT,也是不会创建成功的,也就说是,最后的堆可能有内存碎片,下面也一样。

内存释放函数

void vPortInitialiseBlocks( void )
{
    /* Only required when static memory is not cleared. */
    xNextFreeByte = ( size_t ) 0;
}

可以看出vPortFree并没有具体释放内存的过程,因此如果使用heap_1,一旦申请内存成功就不允许释放。

heap_2

heap_2的内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE

内存块

为了实现内存释放,heap_2引入内存块的概念,每分出去的一段内存就是内存块,为了管理内存块,引入了一个链表结构,此链表是来连接空闲块的,链表中的空闲块顺序是根据空闲块内存的大小排列,链表结构如下:

/* Define the linked list structure.  This is used to link free blocks in order
 * of their size. */
typedef struct A_BLOCK_LINK
{
    struct A_BLOCK_LINK * pxNextFreeBlock; /*<< The next free block in the list. */
    size_t xBlockSize;                     /*<< The size of the free block. */
} BlockLink_t;

为了管理该列表,FreeRTOS定义两个静态变量,xStart、xEnd分别指向链表的头和尾:

/* Create a couple of list links to mark the start and end of the list. */
static BlockLink_t xStart, xEnd;

内存堆初始化函数

内存堆初始化函数为pvHeapInit()

static void prvHeapInit( void )
{
    BlockLink_t * pxFirstFreeBlock;
    uint8_t * pucAlignedHeap;

    /* 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 ) ) );

    /* xStart is used to hold a pointer to the first item in the list of free
     * blocks.  The void cast is used to prevent compiler warnings. */
    xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
    xStart.xBlockSize = ( size_t ) 0;

    /* xEnd is used to mark the end of the list of free blocks. */
    xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
    xEnd.pxNextFreeBlock = NULL;

    /* To start with there is a single free block that is sized to take up the
     * entire heap space. */
    pxFirstFreeBlock = ( void * ) pucAlignedHeap;
    pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
    pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
}

同heap_1一样,pucAlignedHeap指向ucHeap[ portBYTE_ALIGNMENT - 1 ],xStart.pxNextFreeBlock指向pucAlignedHeap ,内存块大小为0,xEnd指向的内存块大小为configADJUSTED_HEAP_SIZE,pxNextFreeBlock 的值为NULL,第一个内存块pxFirstFreeBlock 指向pucAlignedHeap,大小为configADJUSTED_HEAP_SIZE,pxNextFreeBlock 指向xEnd。

内存块插入函数

#define prvInsertBlockIntoFreeList( pxBlockToInsert )                                                                               \
    {                                                                                                                               \
        BlockLink_t * pxIterator;                                                                                                   \
        size_t xBlockSize;                                                                                                          \
                                                                                                                                    \
        xBlockSize = pxBlockToInsert->xBlockSize;                                                                                   \
                                                                                                                                    \
        /* Iterate through the list until a block is found that has a larger size */                                                \
        /* than the block we are inserting. */                                                                                      \
        for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \
        {                                                                                                                           \
            /* There is nothing to do here - just iterate to the correct position. */                                               \
        }                                                                                                                           \
                                                                                                                                    \
        /* Update the list to include the block being inserted in the correct */                                                    \
        /* position. */                                                                                                             \
        pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;                                                             \
        pxIterator->pxNextFreeBlock = pxBlockToInsert;                                                                              \
    }

就是在一个排好序的链表中插入一个数据,xStart最小,for循环一直找到链表中空闲内存块大于pxBlockToInsert内存块大小的内存块,然后插入进去

内存申请函数

void * pvPortMalloc( size_t xWantedSize )
{
    BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink;
    static BaseType_t xHeapHasBeenInitialised = pdFALSE;
    void * pvReturn = NULL;

    vTaskSuspendAll();
    {
        /* If this is the first call to malloc then the heap will require
         * initialisation to setup the list of free blocks. */
        if( xHeapHasBeenInitialised == pdFALSE )
        {
            prvHeapInit();
            xHeapHasBeenInitialised = pdTRUE;
        }

        /* The wanted size must be increased so it can contain a BlockLink_t
         * structure in addition to the requested amount of bytes. */
        if( ( xWantedSize > 0 ) &&
            ( ( xWantedSize + heapSTRUCT_SIZE ) >  xWantedSize ) ) /* Overflow check */
        {
            xWantedSize += heapSTRUCT_SIZE;

            /* Byte alignment required. Check for overflow. */
            if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) )
                    > xWantedSize )
            {
                xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
                configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
            }
            else
            {
                xWantedSize = 0;
            }
        }
        else
        {
            xWantedSize = 0;
        }


        if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
        {
            /* Blocks are stored in byte order - traverse the list from the start
             * (smallest) block until one of adequate size is found. */
            pxPreviousBlock = &xStart;
            pxBlock = xStart.pxNextFreeBlock;

            while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
            {
                pxPreviousBlock = pxBlock;
                pxBlock = pxBlock->pxNextFreeBlock;
            }

            /* If we found the end marker then a block of adequate size was not found. */
            if( pxBlock != &xEnd )
            {
                /* Return the memory space - jumping over the BlockLink_t structure
                 * at its start. */
                pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );

                /* This block is being returned for use so must be taken out of the
                 * list of free blocks. */
                pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;

                /* If the block is larger than required it can be split into two. */
                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;
            }
        }

        traceMALLOC( pvReturn, xWantedSize );
    }
    ( void ) xTaskResumeAll();

    #if ( configUSE_MALLOC_FAILED_HOOK == 1 )
        {
            if( pvReturn == NULL )
            {
                extern void vApplicationMallocFailedHook( void );
                vApplicationMallocFailedHook();
            }
        }
    #endif

    return pvReturn;
}

判断是不是第一次申请内存

xHeapHasBeenInitialised是一个静态变量,初始值为pdFALSE,根据xHeapHasBeenInitialised判断是不是第一个调用pvPortMalloc,如果是,即xHeapHasBeenInitialised=pdFALSE,则调用初始化函数prvHeapInit(),然后将xHeapHasBeenInitialised设置为pdTRRE。

开始分配内存

判断xWantedSize是否大于0,不是的话则xWantedSize=0。在大于0 的情况下,xWantedSize要加上heapSTRUCT_SIZE,heapSTRUCT_SIZE是一个static const类型,值为8,是为了来存储一个BlockLink_t结构,同样分配的内存大小也必须是portBYTE_ALIGNMENT的整数倍。

 if( ( xWantedSize > 0 ) &&
            ( ( xWantedSize + heapSTRUCT_SIZE ) >  xWantedSize ) ) /* Overflow check */
        {
            xWantedSize += heapSTRUCT_SIZE;

            /* Byte alignment required. Check for overflow. */
            if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) )
                    > xWantedSize )
            {
                xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
                configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
            }
            else
            {
                xWantedSize = 0;
            }
        }

如果xWantedSize>0并且xWantedSize要小于xFreeBytesRemaining,xFreeBytesRemaining是一个静态类型变量,表示剩下的可用字节数(所有空闲块大小之和)。
接着按照空闲块大小,从小到大,依次遍历,直到找到一个空闲块,其大小大于等于xWantedSize(此时的xWantedSize=一开始请求的xWantedSize+sizeof(BlockLink_t)),找到满足所需内存块pxBlock,pxPreviousBlock的pxPreviousBlock就指向pxBlock,如果pxBlock不是指向xEnd,就分配内存,pvReturn指向 (pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize的地址,跳过了BlockLink_t,如果pxBlock剩余的内存大于secureheapMINIMUM_BLOCK_SIZE,则将剩余的内存设置成一个新的空闲内存块,新的空闲内存块用 pxNewBlockLink 表示,pxNewBlockLink指向新的内存块的首地址,接着调用prvInsertBlockIntoFreeList,将pxNewBlockLink插入到空闲链表中。

 if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
    {
        /* The wanted size is increased so it can contain a BlockLink_t
         * structure in addition to the requested amount of bytes. */
        if( xWantedSize > 0 )
        {
            xWantedSize += xHeapStructSize;

            /* Ensure that blocks are always aligned to the required number of
             * bytes. */
            if( ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) != 0x00 )
            {
                /* Byte alignment required. */
                xWantedSize += ( secureportBYTE_ALIGNMENT - ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) );
                secureportASSERT( ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) == 0 );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
        {
            /* Traverse the list from the start (lowest address) block until
             * one of adequate size is found. */
            pxPreviousBlock = &xStart;
            pxBlock = xStart.pxNextFreeBlock;

            while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
            {
                pxPreviousBlock = pxBlock;
                pxBlock = pxBlock->pxNextFreeBlock;
            }

            /* If the end marker was reached then a block of adequate size was
             * not found. */
            if( pxBlock != pxEnd )
            {
                /* Return the memory space pointed to - jumping over the
                 * BlockLink_t structure at its start. */
                pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );

                /* This block is being returned for use so must be taken out
                 * of the list of free blocks. */
                pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;

                /* If the block is larger than required it can be split into
                 * two. */
                if( ( pxBlock->xBlockSize - xWantedSize ) > secureheapMINIMUM_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 );
                    secureportASSERT( ( ( ( size_t ) pxNewBlockLink ) & secureportBYTE_ALIGNMENT_MASK ) == 0 );

                    /* 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,xBlockSize=请求的xWantedSize+sizeof(BlockLink_t)

 xFreeBytesRemaining -= pxBlock->xBlockSize;

最后如果使用hook函数,就调用vApplicationMallocFailedHook()

内存释放函数

void vPortFree( void * pv )
{
    uint8_t * puc = ( uint8_t * ) pv;
    BlockLink_t * pxLink;

    if( pv != NULL )
    {
        /* The memory being freed will have an BlockLink_t structure immediately
         * before it. */
        puc -= heapSTRUCT_SIZE;

        /* This unexpected casting is to keep some compilers from issuing
         * byte alignment warnings. */
        pxLink = ( void * ) puc;

        vTaskSuspendAll();
        {
            /* Add this block to the list of free blocks. */
            prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
            xFreeBytesRemaining += pxLink->xBlockSize;
            traceFREE( pv, pxLink->xBlockSize );
        }
        ( void ) xTaskResumeAll();
    }
}

puc为要释放的内存首地址,这就是申请内存返回的pvReturn所指向的地址,所以必须减去heapSTRUCT_SIZE才是要释放的内存段所在内存块的首地址。pxLink 指向真正的释放的内存首地址,将pxLink插入到空闲块链表中,更新xFreeBytesRemaining 。

heap_3

这个分配方法是对标准C库中的mallco和free函数简单封装,FreeRTOS对这两个函数做了线程保护,不分析了。

heap_4

heap_4提供了最优的匹配算法,并且会将碎片合并成一个大的可用内存块,它提供了内存块合并算法

内存堆初始化函数

static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
{
    BlockLink_t * pxFirstFreeBlock;
    uint8_t * pucAlignedHeap;
    size_t uxAddress;
    size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;

    /* Ensure the heap starts on a correctly aligned boundary. */
    uxAddress = ( size_t ) ucHeap;

    if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
    {
        uxAddress += ( portBYTE_ALIGNMENT - 1 );
        uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
        xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
    }

    pucAlignedHeap = ( uint8_t * ) uxAddress;

    /* xStart is used to hold a pointer to the first item in the list of free
     * blocks.  The void cast is used to prevent compiler warnings. */
    xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
    xStart.xBlockSize = ( size_t ) 0;

    /* pxEnd is used to mark the end of the list of free blocks and is inserted
     * at the end of the heap space. */
    uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
    uxAddress -= xHeapStructSize;
    uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
    pxEnd = ( void * ) uxAddress;
    pxEnd->xBlockSize = 0;
    pxEnd->pxNextFreeBlock = NULL;

    /* To start with there is a single free block that is sized to take up the
     * entire heap space, minus the space taken by pxEnd. */
    pxFirstFreeBlock = ( void * ) pucAlignedHeap;
    pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
    pxFirstFreeBlock->pxNextFreeBlock = pxEnd;

    /* Only one block exists - and it covers the entire usable heap space. */
    xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
    xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;

    /* Work out the position of the top bit in a size_t variable. */
    xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}

首先对申请的内存做字节对齐处理,pucAlignedHeap 为内存堆字节对齐以后的可用起始地址,初始化xStart、pxEnd,同heap_2一样,内存块前面会有一个BlockLink_t类型的变量来描述内存块,这里是完成pxFirstFreeBlock 的初始化,xMinimumEverFreeBytesRemaining 记录最小的空闲内存块大小,xFreeBytesRemaining 表示内存堆剩余大小,初始化静态变量xBlockAllocatedBit

内存块插入函数

static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */
{
    BlockLink_t * pxIterator;
    uint8_t * puc;

    /* Iterate through the list until a block is found that has a higher address
     * than the block being inserted. */
    for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
    {
        /* Nothing to do here, just iterate to the right position. */
    }

    /* Do the block being inserted, and the block it is being inserted after
     * make a contiguous block of memory? */
    puc = ( uint8_t * ) pxIterator;

    if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
    {
        pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
        pxBlockToInsert = pxIterator;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    /* Do the block being inserted, and the block it is being inserted before
     * make a contiguous block of memory? */
    puc = ( uint8_t * ) pxBlockToInsert;

    if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
    {
        if( pxIterator->pxNextFreeBlock != pxEnd )
        {
            /* Form one big block from the two blocks. */
            pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
            pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
        }
        else
        {
            pxBlockToInsert->pxNextFreeBlock = pxEnd;
        }
    }
    else
    {
        pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
    }

    /* If the block being inserted plugged a gab, so was merged with the block
     * before and the block after, then it's pxNextFreeBlock pointer will have
     * already been set, and should not be set here as that would make it point
     * to itself. */
    if( pxIterator != pxBlockToInsert )
    {
        pxIterator->pxNextFreeBlock = pxBlockToInsert;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}

下面的代码是遍历空闲内存块链表,找出当前内存块插入点,内存块是按照地址从低到高的顺序链接在一起

   for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
    {
        /* Nothing to do here, just iterate to the right position. */
    }

找到插入点以后判断是否可以和要插入的内存块合并,如果可以的话就合并在一起,接着检查是否可以和下一个内存块合并,如果可以就再次合并,如果不能就将这两个内存块连接起来,pxIterator不等于pxBlockToInsert就意味着在内存块插入的过程中 没有进行过一次内存合并这样的话就使用最普通的处理方法,pxIterator所指向的内存块在前,pxBlockToinsert所指向的内存块在后,将两个内存块链接起来。

heap_5

heap_5 使用了和heap_4相同的合并算法,内存管理实现起来基本相同,但是heap_5内存堆跨越多个不连续的内存段,在使用heap_5调用API函数之前需要调用vPortDefineHeapRegions来对内存做初始化处理,在vPortDefineHeapRegions未执行之前禁止调用任何可能会调用pvPortMalloc的API函数。vPortDefineHeapRegions的参数是一个HeapRegion_t类型的数组,HeapRegion是一个结构体:

typedef struct HeapRegion
{
    uint8_t * pucStartAddress;	//内存块的起始地址	
    size_t xSizeInBytes;	//内存段大小
} HeapRegion_t;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值