1866_FreeRTOS的存储管理方案heap_4分析

Grey

全部学习内容汇总: GitHub - GreyZhang/g_FreeRTOS: learning notes about FreeRTOS.

1866_FreeRTOS的存储管理方案heap_4分析

对FreeRTOS的heap_4进行分析拆解,按照文学式编程的方式重新组织成个人笔记。

主题由来介绍

free以及malloc这样的存储释放以及申请分配机制是很多算法设计实现的基础。 而嵌入式软件中这方面的使用总是有一些局限性,因此能够看到很多种不同的实施方案。 之前在使用FreeRTOS的时候注意到过这个OS中提供了多种方案的实施选择,而其中的 heap_4的方案有着很多有点也是较为推荐的一种实现方案。因此,这里结合源代码来 分析整理一下。整理的过程中我会把这份代码撕裂成碎片,最终理解后进行重组。这样, 后续的这份笔记以及FreeRTOS的这个实现方案就可以纳入到我的工具箱中使用。

要点细节分析

首先写了自己的 Copyright,同时说明一下FreeRTOS中支持的几种heap。关于使用建议,目前来看较为建议的是heap_4.c。 这个在下面提到的链接中可以看到说明。

文件的头部描述

/*
 * FreeRTOS Kernel V10.4.3 LTS Patch 2
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/*
 * A sample implementation of pvPortMalloc() and vPortFree() that combines
 * (coalescences) adjacent memory blocks as they are freed, and in so doing
 * limits memory fragmentation.
 *
 * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the
 * memory management pages of https://www.FreeRTOS.org for more information.
 */

以上是官网相关的文档的说明,可以能够看出几个不同方案的差异点。

处理包含的头文件

  1. stdlib.h
  2. FreeRTOS.h
  3. task.h
  4. MPU_WRAPPERS_INCLUDED_FROM_API_FILE的用途
  • task.h 重定义使用了MPU包装器的接口。不过同头文件的内容看, 这个可能并没有这个作用。 task.h中并没有用到这个宏定义,而task.h中只包含了一个list.h,这里面也 没有对这个宏定义的引用。list.h之中继续看,没有对其他头文件的引用了。这么看,可能这个使用或许还有 其他的细节。或者,这个是一个历史设计痕迹现在已经没有什么用了。从另一个方面来说,FreeRTOS.h中倒是 有可能有用到这个配置选择的地方。因为这个文件中包含的头文件实在是太多了。
  1. <stdlib.h>

    /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
    all the API functions to use the MPU wrappers.  That should only be done when
    task.h is included from an application file. */
    #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE

    "FreeRTOS.h"
    "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

相关参数配置

最小块的大小设置原因:

假设每一个字节有8bit,这个似乎是比较常规的一个定义。

/* Block sizes must not get too small. */
#define heapMINIMUM_BLOCK_SIZE    ( ( size_t ) ( xHeapStructSize << 1 ) )

/* Assumes 8bit bytes! */
#define heapBITS_PER_BYTE         ( ( size_t ) 8 )

heap存储的分配

有两种分配方式,要么是在应用中定义,要么是在这个文件中定义。从分配机制的研究角度 来说没有什么值得区分的,只需要按照在本文件中定义来理解即可。

  /* 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
PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
  #endif /* configAPPLICATION_ALLOCATED_HEAP */

空余内存的链表追踪结构定义

链表是一个单向的链表,指向下一个空余的存储块。同时,数据结构标注了这个存储块的大小。 从注释的信息看,其实这个链表在处理的时候应该会有一个根据地址进行排序的过程。

/* Define the linked list structure.  This is used to link free blocks in order
 * of their memory address. */
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;

内存的释放接口设计

内存释放的同时,需要对剩余内存进行合并处理。如果这一块内容的前面或者后面也是 空余内存,那么应该对其进行合并。

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

/*
 * Inserts a block of memory that is being freed into the correct position in
 * the list of free memory blocks.  The block being freed will be merged with
 * the block in front it and/or the block behind it if the memory blocks are
 * adjacent to each other.
 */
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) PRIVILEGED_FUNCTION;

初始化的处理接口设计

这个会设计成一个隐式调用的方式,在使用相关的API的时候如果发现没有进行过初始 化那么就会对此进行调用。

/*
 * Called automatically to setup the required heap structures the first time
 * pvPortMalloc() is called.
 */
static void prvHeapInit( void ) PRIVILEGED_FUNCTION;

堆中存储块结构体存储大小判断

这是一个固定值,应该按照链表记录节点的数据结构所占空间大小来分配。然而,得同时满足存储的对齐要求。 这一段代码中的括号比较多,使用emacs等具有括号配对判断的工具可以一下子看清楚处理的结构。其实是两 个表达式进行了&操作。前面计算了链表记录节点所占用的存储空间,之后加了一个对齐值之后减一。接下来 的&操作其实就是一个取整的过程。正好是完成了一个存储对齐补充的过程。

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

/* The size of the structure placed at the beginning of each allocated memory
 * block must by correctly byte aligned. */
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );

创建链表的存储位置以及追踪指针

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

存储的分配以及释放回收记录

/* Keeps track of the number of calls to allocate and free memory as well as the
 * number of free bytes remaining, but says nothing about fragmentation. */
PRIVILEGED_DATA static size_t xFreeBytesRemaining = 0U;
PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = 0U;
PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = 0;
PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = 0;

获取一个存储块分配的链表结构中存储占用标志

链表结构中有一个代表块大小的成员,这个成员的最高位用来表征当前的存储块是属于 应用程序的还是已经释放的。这个状态在软件中用一个变量来获取。

/* Gets set to the top bit of an size_t type.  When this bit in the xBlockSize
 * member of an BlockLink_t structure is set then the block belongs to the
 * application.  When the bit is free the block is still part of the free heap
 * space. */
PRIVILEGED_DATA static size_t xBlockAllocatedBit = 0;

实现malloc的接口

传入的参数为申请的内存的字节数目,如果成功则返回一个指向新分配的存储区开始的 指针。这个指针应该是满足系统要求的对齐要求。如果失败,则返回NULL。可以根据配 置选择在分配失败的时候是否调用失败的hook函数。

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

void * pvPortMalloc( size_t xWantedSize )
{
/* local_variables_for_malloc */
<<local_variables_for_malloc>>

vTaskSuspendAll();

/* malloc_memmory_allocation */
<<malloc_memmory_allocation>>

( void ) xTaskResumeAll();

/* call_failed_hook_function_when_configed */
<<call_failed_hook_function_when_configed>>

/* check_malloc_pointer_alignment */
<<check_malloc_pointer_alignment>>

return pvReturn;
}

  1. 分配后的指针对齐检查
  1. ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
  1. 存储分配失败的处理
  1. ( configUSE_MALLOC_FAILED_HOOK == 1 )
    {
        if( pvReturn == NULL )
        {
        extern void vApplicationMallocFailedHook( void );
        vApplicationMallocFailedHook();
        }
        else
        {
        mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
  1. malloc局部变量
  • 针参数。其他的信息暂时并不明朗。因此,按照思考的顺序先定义了用以提供返回值 的指针。
  1. * pvReturn = NULL;
    BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink;
  1. 进行存储分配
  • 0),那么不进行分配。

  • /* auto_init_calling_when_first_time_called */
    <<auto_init_calling_when_first_time_called>>
        /* Check the requested block size is not so large that the top bit is
     * set.  The top bit of the block size member of the BlockLink_t structure
     * is used to determine who owns the block - the application or the
     * kernel, so it must be free. */
        if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
       
        /* calculate_wanted_memory_size_and_make_it_align */
        <<calculate_wanted_memory_size_and_make_it_align>>

        /* try_to_alocate_if_previous_calculation_sucessful */
        <<try_to_alocate_if_previous_calculation_sucessful>>
        }
        else
       
        mtCOVERAGE_TEST_MARKER();
        }

    traceMALLOC( pvReturn, xWantedSize );
    }
  1. 第一次调用时候的初始化处理
  • 链表定义中可以找到。这里的初始化函数,可以在接下来单独实现。同时,这里在设计 的时候考虑了一个可选择配置的TRACE调用。如果只是考虑存储分配的纯粹设计,可以 不用做过多的关注。

  •    /* If this is the first call to malloc then the heap will require
    * initialisation to setup the list of free blocks. */
       if( pxEnd == NULL )
       {
       prvHeapInit();
       }
       else
       {
       mtCOVERAGE_TEST_MARKER();
       }
  1. 需要真实分配的存储大小计算
  •    /* 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 + xHeapStructSize ) >  xWantedSize ) ) /* Overflow check */
       {
       xWantedSize += xHeapStructSize;

       /* Ensure that blocks are always aligned. */
       if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
       {
           /* 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
       {
           mtCOVERAGE_TEST_MARKER();
       }
       }
       else
       {
       xWantedSize = 0;
       }
  1. 计算成功之后进行存储分配
  1. ( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
    {
    /* search_heap_free_memory_list_for_a_big_enough_block */
    <<search_heap_free_memory_list_for_a_big_enough_block>>

    /* allocate_when_free_memory_found */
    <<allocate_when_free_memory_found>>
    }
    else
    {
    mtCOVERAGE_TEST_MARKER();
    }
  1. 查看是否有一个够大的heap内存块
  1. 最后一个块的 pxNextfreeblock 指向 NULL,因此,如果这部分处理到最后的结果 主要 pxBlock 不是NULL就说明找到了对应的存储块。

  •  /* 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;
     }
  1. 找到够大的heap内存块后进行分配并整理内存
  • 比如,如果最小的内存块比所需的内存区还大,那么,可以把内存块进行分割把多出来 的补充到后面的存储中。最后,对于当前存储的剩余信息等统计数值也一并做更新。

  •    /* 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 ) > 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 );
           configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_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 );
       }
       else
       {
           mtCOVERAGE_TEST_MARKER();
       }

       xFreeBytesRemaining -= pxBlock->xBlockSize;

       if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
       {
           xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
       }
       else
       {
           mtCOVERAGE_TEST_MARKER();
       }

       /* The block is being returned - it is allocated and owned
        * by the application and has no "next" block. */
       pxBlock->xBlockSize |= xBlockAllocatedBit;
       pxBlock->pxNextFreeBlock = NULL;
       xNumberOfSuccessfulAllocations++;
       }
       else
       {
       mtCOVERAGE_TEST_MARKER();
       }

实现free接口

free的实现其实比较简单,只是把对应的存储标记信息修改一下然后加入到free memory 的链表之中。不过,安全起见,增加一些必要的检查。

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

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 -= xHeapStructSize;

    /* This casting is to keep the compiler from issuing warnings. */
    pxLink = ( void * ) puc;

    /* Check the block is actually allocated. */
    configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
    configASSERT( pxLink->pxNextFreeBlock == NULL );

    if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
    {
    if( pxLink->pxNextFreeBlock == NULL )
    {
        /* The block is being returned to the heap - it is no longer
         * allocated. */
        pxLink->xBlockSize &= ~xBlockAllocatedBit;

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

初始化以及状态查询接口实现

这部分设计的实现是比较简单的,基本上都是变量的信息查询。而初始化是不必要的, 因为在malloc的实现中借助于链表初始化的状态信息来判断了是否有初始化的必要, 以此实现了初始化的自动调用。

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

size_t xPortGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/

size_t xPortGetMinimumEverFreeHeapSize( void )
{
return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/

void vPortInitialiseBlocks( void )
{
/* This just exists to keep the linker quiet. */
}

heap的初始化设计

这个初始化主要是初始化链表以及分配的存储之间的关系。

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

static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
{
/* local variables needed for initialization */
size_t uxAddress;
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
uint8_t * pucAlignedHeap;
BlockLink_t * pxFirstFreeBlock;

/* check_alignment_of_heap_memory */
<<check_alignment_of_heap_memory>>

/* heap_memory_linked_list_init */
<<heap_memory_linked_list_init>>

<<heap_allocation_state_with_linked_list_and_variables>>
}

  1. heap存储的对齐检查与处理
  • 理。这会讲heap开始的位置向后移动几个字节。相应的,总体的heap空间也会略有减少。 通过这个处理, pucAlignedHeap将会存储符合对齐要求的heap的起始位置。而 xTotalheapsize 也会完成数值的调整用来指示总体的heap大小。
  • 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;
  1. heap链表初始状态的建立
  1. 用来存储指向第一个对象的空余存储块链表,pxEnd用来标记空余存储块链表的 结束并且插入到heap空间最后。这里设置了 xBlocksize 的数值全都是0,代表存储 其实是free的状态。pxEnd插入的时候,也考虑了对齐处理,算是防御式的编程了。
  • 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;
  1. 使用链表以及状态信息描述初始化时候的heap存储状态
  • heap存储其实只有一个块占用了除了pxEnd标记的最后一个块之外的所有的存储。 这样,需要对这个唯一的块进行记录信息的标记。这也是 pxFirstfreeblock 这个临 时的指针变量出现的原因。这样,第一个存储块的地址信息、大小信息以及下一个空 余内存块的信息写入到了满足对齐的heap存储中。两个变量记录了free的内存中的最小 块大小以及全部的free内存大小。而计算出来的 xBlockallocatedbit 其实就是一个 最高位为1其他位为0的size_t的数值。这个后面用来标注存储是否被用过。

  • 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 );

向空闲存储链表中插入一个内存块

这是一个很容易实现的操作,首先找到插入点,之后修改链表节点的链接属性即可。 由于存储剩余空间的统计都是在申请或者释放内存的时候处理的,因此这里就是一个纯 粹的链表操作。插入点如何找呢?只需要找到第一个next属性大于被插入地址的位置即可。 这里没有等于的可能性,否则是一种错误。因此,虽然条件判断利用了小于而不是小于 等于,其实找到的应该是next第一个大于需要插入的存储块地址的节点。找到之后,会 面临两类3种情况。分别是可以合并,不能合并。其中,可以合并的情况可能是能与前面 合并或者能与后面合并。这两种全都进行了判断处理,因此可以时间两个方向的合并。 至于无法合并的情况,可能是前后位置全都有被分配出去的内存。这种情况下,只修改 链表中的链接关系即可,不需要调整任何大小信息或者进行节点的合并。

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

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();
}
}

heap统计功能

这不是这一套机制的主要部分,而是用以使用这一套机制的一个分析工具。

  1. 统计代码实现


  • void vPortGetHeapStats( HeapStats_t * pxHeapStats )
    {
    BlockLink_t * pxBlock;
    size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */

    vTaskSuspendAll();
    {
        pxBlock = xStart.pxNextFreeBlock;

        /* pxBlock will be NULL if the heap has not been initialised.  The heap
     * is initialised automatically when the first allocation is made. */
        if( pxBlock != NULL )
        {
        do
        {
            /* Increment the number of blocks and record the largest block seen
         * so far. */
            xBlocks++;

            if( pxBlock->xBlockSize > xMaxSize )
            {
            xMaxSize = pxBlock->xBlockSize;
            }

            if( pxBlock->xBlockSize < xMinSize )
            {
            xMinSize = pxBlock->xBlockSize;
            }

            /* Move to the next block in the chain until the last block is
         * reached. */
            pxBlock = pxBlock->pxNextFreeBlock;
        } while( pxBlock != pxEnd );
        }
    }
    ( void ) xTaskResumeAll();

    pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize;
    pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize;
    pxHeapStats->xNumberOfFreeBlocks = xBlocks;

    taskENTER_CRITICAL();
    {
        pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining;
        pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations;
        pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees;
        pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining;
    }
    taskEXIT_CRITICAL();
    }
  1. 涉及到的数据结构
  • Used to pass information about the heap out of vPortGetHeapStats(). */
    typedef struct xHeapStats
    {
    size_t xAvailableHeapSpaceInBytes;      The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */
    size_t xSizeOfLargestFreeBlockInBytes;  The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */
    size_t xSizeOfSmallestFreeBlockInBytes; The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */
    size_t xNumberOfFreeBlocks;             The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */
    size_t xMinimumEverFreeBytesRemaining;  The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */
    size_t xNumberOfSuccessfulAllocations;  The number of calls to pvPortMalloc() that have returned a valid memory block. */
    size_t xNumberOfSuccessfulFrees;        The number of calls to vPortFree() that has successfully freed a block of memory. */
    } HeapStats_t;

代码构建

/* heap_4_c_lang_header */
<<heap_4_c_lang_header>>

  /* heap_4_included_files */
<<heap_4_included_files>>

  /* check_if_use_is_valid */
<<check_if_use_is_valid>>

  /* pars_def */
<<pars_def>>

  /* memory_allocation */
<<memory_allocation>>

  /* free_memory_linked_list */
<<free_memory_linked_list>>

  /* memory_free_and_merge_declaration */
<<memory_free_and_merge_declaration>>

  /* auto_init */
<<auto_init>>

  /* heap_struct_memory_size */
<<heap_struct_memory_size>>

  /* linked_list_and_ref_pointer */
<<linked_list_and_ref_pointer>>

  /* mem_allocation_and_free_track_states */
<<mem_allocation_and_free_track_states>>

  /* block_allocated_bit */
<<block_allocated_bit>>

  /* freertos_malloc */
<<freertos_malloc>>

  /* freertos_free */
<<freertos_free>>

  /* init_funcs_and_check_funcs */
<<init_funcs_and_check_funcs>>

  /* freertos_heap_init */
<<freertos_heap_init>>

/* insert_a_block_into_free_list */
<<insert_a_block_into_free_list>>

/* heap_status_statistic */
<<heap_status_statistic>>

小结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/299355.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

辗转相除法求最大公约数

介绍 辗转相除法&#xff08;又称欧几里德算法&#xff09;是一种求最大公约数的算法。它基于这样一个事实&#xff1a;两个数的最大公约数等于较大数和较小数余数的最大公约数。即两个数相除&#xff0c;再将除数和余数反复相除&#xff0c;当余数为0时&#xff0c;取当前除法…

Python异常捕获和处理语句 try-except-else-finally

目录 try-except-else-finally语句 1. 基本用法 2. 多个异常处理 3. 处理所有其他异常 4. 多个except子句 5. 使用else子句 6. 使用finally子句 7. 使用as关键字 实例 例1 例2 例3 例4 例5 例6 例7 例8 结论 try-except-finally语句 在Python中&#xff0c;try-e…

概率论与数理统计 知识点+课后习题

文章目录 &#x1f496; [学习资源整合](https://www.cnblogs.com/duisheng/p/17872980.html)&#x1f4da; 总复习&#x1f4d9; 选择题&#x1f4d9; 填空题&#x1f4d9; 大题1. 概率2. 概率3. 概率4. P5. 概率6. 概率密度函数 F ( X ) F(X) F(X)7. 分布列求方差 V ( X ) …

【STM32】STM32学习笔记-DMA直接存储器存储(23)

00. 目录 文章目录 00. 目录01. DMA简介02. DMA主要特性03. 存储器映像04. DMA框图05. DMA基本结构06. DMA请求07. 数据宽度与对齐08. 数据转运DMA09. ADC扫描模式DMA10. 附录 01. DMA简介 小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F101xx、STM32F102xx和STM32F…

Hive11_Rank函数

Rank 1&#xff09;函数说明 RANK() 排序相同时会重复&#xff0c;总数不会变 DENSE_RANK() 排序相同时会重复&#xff0c;总数会减少 ROW_NUMBER() 会根据顺序计算 2&#xff09;数据准备 3&#xff09;需求 计算每门学科成绩排名。 4&#xff09;创建本地 score.txt&…

C语言程序设计——数学运算

基本运算符 运算符说明例子赋值运算符a b;、-、*、/、()基本四则运算a (a c) * d;%取余运算符a b % 2&、^、~、l位运算a ~b l c>>、<<左移和右移a b >> 2 在c语言的数学运算中&#xff0c;所涉及到的符号如图所示&#xff0c;在使用过程中应该了…

编程语言的发展趋势和未来方向

1、编程语言的未来&#xff1f; 随着科技的飞速发展&#xff0c;编程语言在计算机领域中扮演着至关重要的角色。它们是软件开发的核心&#xff0c;为程序员提供了与机器沟通的桥梁。那么&#xff0c;在技术不断进步的未来&#xff0c;编程语言的走向又将如何呢&#xff1f; 方…

宋仕强论道之华强北后山寨手机时代(三十六)

今天继续讲华强北山寨手机&#xff0c;跟手机配套周边产品。华强北&#xff0c;作为中国电子产品的集散地和创新中心&#xff0c;一直以来都是电子产品和数码产品的聚集地。在早期&#xff0c;赛格市场以其走私、翻新的电脑和电脑周边产品而闻名。赛格大厦以前5楼以上都是做电脑…

Hive实战:网址去重

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、启动Hive Metastore服务2、启动Hive客户端3、基于HDFS数据文件创建Hive外部表4、利用Hive SQL实…

11.3编写Linux串口驱动

编写串口驱动主要步骤 构建并初始化 struct console 对象&#xff0c;若串口无需支持 console 可省略此步骤 //UART驱动的console static struct uart_driver virt_uart_drv; static struct console virt_uart_console {//console 的名称&#xff0c;配合index字段使用&…

uniapp 解决安卓App使用uni.requestPayment实现沙箱环境支付宝支付报错

背景&#xff1a;uniapp与Java实现的安卓端app支付宝支付&#xff0c;本想先在沙箱测试环境测支付&#xff0c;但一直提示“商家订单参数异常&#xff0c;请重新发起付款。”&#xff0c;接着报错信息就是&#xff1a;{ "errMsg": "requestPayment:fail [pa…

Archlinux下自启动rclone mount

路径&#xff1a; /etc/systemd/system/rclonemount.service [Unit] Descriptionrclonemount Requiresnetwork-online.target.wants Afteralist.service[Service] Typesimple ExecStartPre/bin/mkdir -p /media ExecStart/usr/bin/rclone mount \aliyun: /media \--config /ro…

Video-GroundingDino论文解读

文章目录 前言一、摘要二、引言三、贡献四、模型结构1、模型定义与问题数据少问题模型解决问题模型模块 2、模型结构模型结构图Cross-Modality Spatio-Temporal EncoderLanguage-Guided Query SelectionCross-Modality Spatio-Temporal DecoderPrediction Heads 总结 前言 之前…

冬天夺去的清爽,可爱,春天都会还给你

这款外套上身可太时尚好看了 春天日常穿着或者出行游玩 应对早晚温差&#xff0c;兼具时尚和温度两不误 干净率性闲适的洒脱范整件衣服干净不失细节 下摆有橡筋收紧更加保暖了工艺方面也毫不逊色&#xff0c;防水拉链 四合扣、猪鼻扣一应俱全简直就是一件实用与时尚并存的…

Grind75第2天 | 238.除自身以外数组的乘积、75.颜色分类、11.盛最多水的容器

238.除自身以外数组的乘积 题目链接&#xff1a;https://leetcode.com/problems/product-of-array-except-self 解法&#xff1a; 这个题有follow up&#xff0c; 要求优化到空间复杂度为O(1)&#xff0c;所以给出baseline和follow up的解法。 Baseline&#xff1a;利用索引…

网络报文分析程序的设计与实现(2024)

1.题目描述 在上一题的基础上&#xff0c;参照教材中各层报文的头部结构&#xff0c;结合使用 wireshark 软件&#xff08;下载地址 https://www.wireshark.org/download.html#releases&#xff09;观察网络各层报文捕获&#xff0c;解析和分析的过程&#xff08;如下 图所示&a…

SpringBoot+Redis实现接口防刷功能

场景描述&#xff1a; 在实际开发中&#xff0c;当前端请求后台时&#xff0c;如果后端处理比较慢&#xff0c;但是用户是不知情的&#xff0c;此时后端仍在处理&#xff0c;但是前端用户以为没点到&#xff0c;那么再次点击又发起请求&#xff0c;就会导致在短时间内有很多请求…

FCN-8s源码理解

FCN网络用于对图像进行分割&#xff0c;由于是全卷积网络&#xff0c;所以对输入图像的分辨率没有要求。本文重点对fcn8s.py中图像降采样和上采样后图像分辨率的变换进行理解。 相关知识 为准确理解图像分辨率的变换&#xff0c;对网络结构中影响图像分辨率变换的几个函数进行…

leetcode:3. 无重复字符的最长子串

一、题目 二、函数原型 int lengthOfLongestSubstring(char* s) 三、思路 本题就是找最长的无重复字符子串。 两层循环&#xff0c;外层循环控制字串的起始位置&#xff0c;内层循环控制字串的长度。 设置一个长度为256且初始为0的hash表&#xff08;因为一共有256个字符…

windows----Vmware虚拟机安装ubuntu

双系统来回切有点麻烦&#xff0c;还是安装虚拟机先整个简单的。 1 安装Vmware17虚拟机 虚拟机下载网址&#xff0c;一直下一步就行&#xff0c;更新和加入计划关闭 秘钥&#xff1a;MC60H-DWHD5-H80U9-6V85M-8280D 2 下载ubantu镜像 浙大镜像&#xff0c;自己选择版本吧&a…