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.
*/
以上是官网相关的文档的说明,可以能够看出几个不同方案的差异点。
处理包含的头文件
- stdlib.h
- FreeRTOS.h
- task.h
- MPU_WRAPPERS_INCLUDED_FROM_API_FILE的用途
- task.h 重定义使用了MPU包装器的接口。不过同头文件的内容看, 这个可能并没有这个作用。 task.h中并没有用到这个宏定义,而task.h中只包含了一个list.h,这里面也 没有对这个宏定义的引用。list.h之中继续看,没有对其他头文件的引用了。这么看,可能这个使用或许还有 其他的细节。或者,这个是一个历史设计痕迹现在已经没有什么用了。从另一个方面来说,FreeRTOS.h中倒是 有可能有用到这个配置选择的地方。因为这个文件中包含的头文件实在是太多了。
- <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;
}
- 分配后的指针对齐检查
- ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
- 存储分配失败的处理
- ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
- malloc局部变量
- 针参数。其他的信息暂时并不明朗。因此,按照思考的顺序先定义了用以提供返回值 的指针。
- * pvReturn = NULL;
BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink;
- 进行存储分配
- 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 );
}
- 第一次调用时候的初始化处理
- 链表定义中可以找到。这里的初始化函数,可以在接下来单独实现。同时,这里在设计 的时候考虑了一个可选择配置的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();
}
- 需要真实分配的存储大小计算
- /* 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;
}
- 计算成功之后进行存储分配
- ( ( 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();
}
- 查看是否有一个够大的heap内存块
- 最后一个块的 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;
}
- 找到够大的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>>
}
- 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;
- heap链表初始状态的建立
- 用来存储指向第一个对象的空余存储块链表,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;
- 使用链表以及状态信息描述初始化时候的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统计功能
这不是这一套机制的主要部分,而是用以使用这一套机制的一个分析工具。
- 统计代码实现
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();
}
- 涉及到的数据结构
- 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>>
小结