堆内存管理代码具体实现
- heap_1
- 内存申请函数
- 内存释放函数
- heap_2
- 内存块
- 内存堆初始化函数
- 内存块插入函数
- 内存申请函数
- 判断是不是第一次申请内存
- 开始分配内存
- 内存释放函数
- heap_3
- heap_4
- 内存堆初始化函数
- 内存块插入函数
- heap_5
上一篇文章说了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;}}}#endifvTaskSuspendAll();{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();}}#endifreturn 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();}}#endifreturn 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;