FreeRTOS学习——内存管理heap4,仅用于记录自己阅读与学习源码
FreeRTOS Kernel V10.5.1
port :GCC/ARM_CM7
参考:
FreeRTOS:4.内存管理_freertos heap4内存管理-CSDN博客
FreeRTOS内存管理之heap_4.c_freertos heap4源码解读-CSDN博客
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
#ifndef configHEAP_CLEAR_MEMORY_ON_FREE
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
#endif
heapMINIMUM_BLOCK_SIZE 宏
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
xHeapStructSize
乘以2。xHeapStructSize
表示存储堆信息的数据结构(BlockLink_t
)的大小。heapMINIMUM_BLOCK_SIZE
确保内存分配的最小单位至少能够存储两个这样的数据结构,这样即使在最坏的情况下,内存也能被有效管理,避免过小的内存块导致碎片化严重。size_t类型
1.无符号整数:size_t
是无符号的,这意味着它只能表示非负值。
2.平台相关:size_t
的具体大小(即它是多少位的整数)取决于编译器和目标平台。例如,在32位系统上,size_t
通常是32位的;在64位系统上,size_t
通常是64位的
heapBITS_PER_BYTE 宏
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
heapSIZE_MAX 宏
#define heapSIZE_MAX ( ~( ( size_t ) 0 ) )
size_t
类型能够表示的最大值。~( ( size_t ) 0 )
是一个位运算,对size_t
类型的0进行按位取反操作,得到的就是该类型的全1,即最大值。size_t
类型的表示范围。heapMULTIPLY_WILL_OVERFLOW 宏
#define heapMULTIPLY_WILL_OVERFLOW( a, b ) ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) )
a
大于0且b
大于heapSIZE_MAX / a
,则相乘的结果会超出size_t
类型的表示范围,引发溢出。size_t
类型的表示范围,以防止潜在的溢出错误。heapADD_WILL_OVERFLOW 宏
#define heapADD_WILL_OVERFLOW( a, b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
a
大于heapSIZE_MAX - b
,则相加的结果会超出size_t
类型的表示范围,引发溢出。size_t
类型的表示范围,以防止潜在的溢出错误。BlockLink_t 结构中 xBlockSize 成员的 MSB 用于跟踪块的分配状态。
当 BlockLink_t 结构的 xBlockSize 成员的 MSB 位被设置时,该块属于应用程序。
当该位为空闲时,该数据块仍是空闲堆空间的一部分。
heapBLOCK_ALLOCATED_BITMASK 宏定义了一个位掩码,用于标记一个内存块是否被分配。具体来说,它是通过将一个size_t
类型的1左移(sizeof(size_t) * heapBITS_PER_BYTE) - 1
位得到的。sizeof(size_t)
返回size_t
类型的大小(字节数),乘以heapBITS_PER_BYTE
(在本代码中定义为8,表示一个字节有8位)再减去1,得到的是size_t
类型的最高位的位索引。因此,这个宏定义的值是一个只有最高位为1,其余位为0的掩码。
heapBLOCK_SIZE_IS_VALID 宏用于检查一个给定的块大小值是否有效。这个宏通过将给定的块大小值xBlockSize
与heapBLOCK_ALLOCATED_BITMASK
进行按位与运算,并检查结果是否为0来实现这一点。如果最高位为0,则块大小被认为是有效的(即该块未被分配),否则无效(已被分配)。
heapBLOCK_IS_ALLOCATED 宏用于检查一个特定的内存块是否已被分配。它同样使用按位与运算heapBLOCK_ALLOCATED_BITMASK
与内存块大小pxBlock->xBlockSize
进行操作。如果最高位为1,则返回真(非0值),表示该块已被分配;如果最高位为0,则返回假(0值),表示该块未被分配。
heapALLOCATE_BLOCK 宏用于将一个内存块标记为已分配。通过将内存块大小pxBlock->xBlockSize
与heapBLOCK_ALLOCATED_BITMASK
按位或运算,可以将xBlockSize
的最高位置为1,从而标记该块已被分配。
heapFREE_BLOCK 宏用于将一个内存块标记为未分配。通过将内存块大小pxBlock->xBlockSize
与heapBLOCK_ALLOCATED_BITMASK
按位非运算后再按位与运算,可以将xBlockSize
的最高位清零,从而标记该块未被分配。
/* MSB of the xBlockSize member of an BlockLink_t structure is used to track
* the allocation status of a block. When MSB of 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. *//*BlockLink_t 结构中 xBlockSize 成员的 MSB 用于跟踪块的分配状态。
* 当 BlockLink_t 结构的 xBlockSize 成员的 MSB 位被设置时,该块属于应用程序。
* 当该位为空闲时,该数据块仍是空闲堆空间的一部分。*/
#define heapBLOCK_ALLOCATED_BITMASK ( ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ) )
#define heapBLOCK_SIZE_IS_VALID( xBlockSize ) ( ( ( xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) == 0 )
#define heapBLOCK_IS_ALLOCATED( pxBlock ) ( ( ( pxBlock->xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) != 0 )
#define heapALLOCATE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) |= heapBLOCK_ALLOCATED_BITMASK )
#define heapFREE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) &= ~heapBLOCK_ALLOCATED_BITMASK )
/* 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;
xHeapStructSize
:
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
这段代码计算了放置在每个已分配内存块开头的结构的大小,并确保它按字节正确对齐。sizeof( BlockLink_t )
获取了 BlockLink_t
结构的原始大小。( portBYTE_ALIGNMENT - 1 )
用于计算需要对齐的额外字节数。~( ( size_t ) portBYTE_ALIGNMENT_MASK )
是一个按位与操作,用于将计算出的大小截断到最近的字节对齐边界。这样可以确保每个分配的内存块都从正确的字节边界开始,以避免在某些处理器上可能出现的对齐错误。
#define portBYTE_ALIGNMENT 8 //按8字节对齐
#define portBYTE_ALIGNMENT_MASK ( 0x0007 ) // 0000 0111
这段代码的结果是xHeapStructSize为8字节
例:
- 例如,假设 `sizeof( BlockLink_t )` 是 10 字节,`portBYTE_ALIGNMENT` 是 4(4字节对齐),那么 `sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) )` 是 13 字节。
- `13 & 0xFFFFFFFC` 的结果是 12 字节,因为 13 的二进制表示是 `1101`,截断到 4 字节对齐边界的结果是 `1100`,即 12 字节。
**xStart
和 pxEnd
**:
PRIVILEGED_DATA static BlockLink_t xStart;
PRIVILEGED_DATA static BlockLink_t * pxEnd = NULL;
这里创建了两个 BlockLink_t
类型的静态变量来标记内存分配列表的开始和结束。xStart
作为链表的头节点,指向第一个空闲内存块。pxEnd
则是一个指向链表尾节点的指针,初始值为 NULL
,表示链表尚未初始化。
除了xStart以外,其余内存控制块都是在内存池中,用指针的形式进行访问。也就是说,xStart是作为哨兵节点,xStart的pxNextFreeBlock指针就是第一个空闲块节点。
内存分配和释放的统计信息:
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;
这些静态变量用于跟踪内存分配和释放的状态信息:
xFreeBytesRemaining
记录了当前剩余的空闲内存字节数。xMinimumEverFreeBytesRemaining
记录了自启动以来剩余空闲内存字节数的最小值。xNumberOfSuccessfulAllocations
记录了成功的内存分配次数。xNumberOfSuccessfulFrees
记录了成功的内存释放次数。static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
{
BlockLink_t * pxFirstFreeBlock; /*指向第一个空闲块的指针*/
uint8_t * pucAlignedHeap; /*指向对齐的堆空间的指针*/
portPOINTER_SIZE_TYPE uxAddress; /*用于地址计算的变量,是一个地址*/
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;/*总堆空间大小*/
/* Ensure the heap starts on a correctly aligned boundary. * 确保堆从正确对齐的边界开始*/
uxAddress = ( portPOINTER_SIZE_TYPE ) ucHeap;/*数组名是地址,将ucHeap的地址转换为portPOINTER_SIZE_TYPE类型*/
/**堆内存地址对齐*/
if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
{
/*调整地址以确保对齐*/
uxAddress += ( portBYTE_ALIGNMENT - 1 );
uxAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
xTotalHeapSize -= uxAddress - ( portPOINTER_SIZE_TYPE ) ucHeap;//重新计算总堆空间大小
}
pucAlignedHeap = ( uint8_t * ) uxAddress;//将对齐后的地址赋值给pucAlignedHeap
/**设置xStart和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 用于保存指向空闲块列表中第一个项的指针。 void cast 用于防止编译器发出警告*/
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;//将xStart.pxNextFreeBlock设为对齐后的堆地址
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. * pxEnd 用于标记空闲块列表的结束,并插入堆空间的末尾。*/
uxAddress = ( ( portPOINTER_SIZE_TYPE ) pucAlignedHeap ) + xTotalHeapSize;//计算堆结束地址
uxAddress -= xHeapStructSize;//减去BlockLink_t结构大小,为pxEnd提供空间
uxAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );//对齐
pxEnd = ( BlockLink_t * ) uxAddress;//将pxEnd指向对齐后的地址
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. * 首先有一个单独的空闲块,其大小为占用整个堆空间,减去 pxEnd 占用的空间*/
pxFirstFreeBlock = ( BlockLink_t * ) pucAlignedHeap;//pucAlignedHeap指向对齐后的堆地址
pxFirstFreeBlock->xBlockSize = ( size_t ) ( uxAddress - ( portPOINTER_SIZE_TYPE ) pxFirstFreeBlock );
pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
/* Only one block exists - and it covers the entire usable heap space.
* 只存在一个数据块,它覆盖了整个可用堆空间*/
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
}
创建了一个空闲块列表,并将整个可用堆空间作为一个空闲块放入列表中
void * pvPortMalloc( size_t xWantedSize )
{
BlockLink_t * pxBlock; /*找到的空闲块指针*/
BlockLink_t * pxPreviousBlock; /*前一个块指针*/
BlockLink_t * pxNewBlockLink; /*新块链接指针*/
void * pvReturn = NULL; /*返回值指针*/
size_t xAdditionalRequiredSize; /*额外所需大小*/
vTaskSuspendAll();//暂停任务调度,以确保在此期间没有任务切换
{
/* If this is the first call to malloc then the heap will require
* initialisation to setup the list of free blocks. */ /*检查 pxEnd 是否为 NULL,如果是,则是第一次调用 malloc,则堆将需要初始化以设置空闲块列表。*/
if( pxEnd == NULL )
{
prvHeapInit();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*计算所需内存大小 xWantedSize */ if( xWantedSize > 0 )
{
/* The wanted size must be increased so it can contain a BlockLink_t
* structure in addition to the requested amount of bytes. Some * additional increment may also be needed for alignment. */ /*计算附加的所需大小 xAdditionalRequiredSize,
* 这个大小包括 BlockLink_t 结构体的大小和字节对齐所需的额外空间*/
xAdditionalRequiredSize = xHeapStructSize + portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK );
if( heapADD_WILL_OVERFLOW( xWantedSize, xAdditionalRequiredSize ) == 0 )
{
xWantedSize += xAdditionalRequiredSize;
}
else
{
xWantedSize = 0;
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Check the block size we are trying to allocate 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( heapBLOCK_SIZE_IS_VALID( xWantedSize ) != 0 )
{
/*分配的内存大小大于 0 并且小于或等于剩余的空闲字节数*/
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. */ /*返回指向的内存空间 - 从 BlockLink_t 结构的开始跳过去,
* 即直接返回内存空间地址,返回的内存空间不包含 BlockLink_t 结构*/
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. */ /*将pxBlock从空闲块列表中移除*/
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. */ /*将找到的空闲块标记为已分配状态*/
heapALLOCATE_BLOCK( pxBlock );
pxBlock->pxNextFreeBlock = NULL;
xNumberOfSuccessfulAllocations++;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
traceMALLOC( pvReturn, xWantedSize );
}
/*恢复调度器,允许任务切换*/
( void ) xTaskResumeAll();
/*调用内存分配失败钩子函数*/
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL ) { vApplicationMallocFailedHook(); } else { mtCOVERAGE_TEST_MARKER(); } } #endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
return pvReturn;
}
BlockLink_t
结构体和对齐要求。注:如果没有设置这个宏configHEAP_CLEAR_MEMORY_ON_FREE的话,free 的内存块并没有清0,只是把它重新插到空闲块链表中,并标记为空闲状态
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. */ /*由于 pvPortMalloc 分配的内存块前有一个 BlockLink_t 结构,
* 这里将 puc 向前移动 xHeapStructSize 个字节以指向该结构,也是内存块的起始地址*/
puc -= xHeapStructSize;
/* This casting is to keep the compiler from issuing warnings. */
pxLink = ( void * ) puc;
/*这两个断言用于确保要释放的内存块确实已经被分配(即 xBlockSize 的最高位被设置)
* 并且目前不在空闲块链表中(即 pxNextFreeBlock 为 NULL)*/
configASSERT( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL );
if( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 )
{
if( pxLink->pxNextFreeBlock == NULL )
{
/* The block is being returned to the heap - it is no longer
* allocated. */ /*标记内存块为未分配状态*/
heapFREE_BLOCK( pxLink );
/*如果配置 configHEAP_CLEAR_MEMORY_ON_FREE 为 1,
* 则使用 memset 将释放的内存块内容清零,以确保数据安全。*/
#if ( configHEAP_CLEAR_MEMORY_ON_FREE == 1 )
{
( void ) memset( puc + xHeapStructSize, 0, pxLink->xBlockSize - xHeapStructSize ); } #endif
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();
}
}
}
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. */
/*从空闲内存块列表的起始块 xStart 开始遍历。
寻找合适的位置,使得要插入的内存块 pxBlockToInsert 的地址大于当前遍历到的内存块 pxIterator,
但是小于下一个内存块 pxIterator->pxNextFreeBlock 的地址。
这确保了内存块列表始终按内存地址从小到大排序。*/
}
/* Do the block being inserted, and the block it is being inserted after
* make a contiguous block of memory? */ /*插入的数据块和其后插入的数据块是否是一个连续的内存块*/
puc = ( uint8_t * ) pxIterator;
/*如果 pxIterator 的结束地址与 pxBlockToInsert 的起始地址相同,则它们是相邻的空闲块*/
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
/*合并 pxIterator 和 pxBlockToInsert*/ pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
pxBlockToInsert = pxIterator;/*将 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;
/*如果 pxBlockToInsert 的结束地址与 pxIterator->pxNextFreeBlock 的起始地址相同,则它们是相邻的空闲块*/
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
{
if( pxIterator->pxNextFreeBlock != pxEnd )
{
/* Form one big block from the two blocks. */
/*合并 pxBlockToInsert 和 pxIterator->pxNextFreeBlock */ pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}
else
{
/*如果 pxIterator->pxNextFreeBlock 是最后一个空闲块(即 pxEnd),
* 则将 pxBlockToInsert->pxNextFreeBlock 直接指向 pxEnd。*/
pxBlockToInsert->pxNextFreeBlock = pxEnd;
}
}
else
{
/*插入到 pxIterator 和 pxIterator->pxNextFreeBlock 之间*/
/*如果和右边的不能合并,则将 pxBlockToInsert的pxNextFreeBlock指向pxIterator->pxNextFreeBlock*/
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 和 pxBlockToInsert 不相同,说明没合并,将pxBlockToInsert插入到pxIterator之后。
* 上面通过检查有没有和右边合并,已经维护了pxBlockToInsert->pxNextFreeBlock的正确性
* 这样就将 pxBlockToInsert 插入到 pxIterator 和 pxIterator->pxNextFreeBlock 之间了。
如果 pxIterator 和 pxBlockToInsert 相同,说明该内存块已经被合并,不需要重新插入。*/
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
heap4_c所谓的空闲内存可合并,只是合并相邻的内存碎片,这是基于内存池的线性连续而设计的。
因此,为了尽可能的减少内存碎片,提升内存合并的作用,尽可能把上电后不释放的动态内存在初始化阶段申请(比如说动态分配的任务,包括TCB和任务栈),然后对于重复申请释放的动态内存,在初始化阶段结束后再分配和使用。也就是说,堆的前半部分内存都用于不释放的动态内存,然后后半部分就用来一些频繁申请释放的动态内存
从 FreeRTOS 的堆管理中收集统计信息,并将其存储在一个 HeapStats_t
结构体中。具体来说,它遍历空闲内存块链表,统计空闲块的数量,计算最大的空闲块和最小的空闲块的大小,最后将这些信息以及总的可用堆空间大小、成功的内存分配和释放次数、历史上最少的空闲堆空间大小存储到 HeapStats_t
结构体中。通过暂停任务调度和使用临界区,确保了在统计期间堆的状态不会被其他任务或中断改变,从而保证了统计信息的准确性
/* 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;
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. */ /* pxBlock 为 NULL 的情况表示堆尚未初始化。堆会在第一次内存分配时自动初始化。 */ if( pxBlock != NULL )
{
/*遍历空闲块链表,直到找到 pxEnd 结束块*/
while( pxBlock != pxEnd )
{
/* 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;
}
}
}
( 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();
}