由于发现FreeRTOS(v9.0.0)中并没有对realloc进行支持,所以就自己写了pvPortRealloc函数。
首先先谈谈Heap4内存管理机制,由xStart为头结点,pxEnd为尾节点构成的一条链表。用来描述ucheap数组中尚未分配的地址块。这些地址块按照地址从小到大插入到链表中。
当使用pvPortMalloc 申请N个字节数据时,首先会添加一个BlockLink_t结构,其中包含字段xBlockSize和*pxNextFreeBlock。实际上我们申请块的大小为N+sizeof(BlockLink) +( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ))。后面括号中表示8字节对齐。所以我们从xStart头结点开始遍历,直到找到块大小大于xBlockSize的节点。取出该空闲块,其中一部分用作于申请的块,剩下的内存块组成新的空闲块,按照地址大小,插入到链表中。
当使用vPortFree释放某一块内存时,同样会先获得其BlockLink_t结构体,将该块作为空闲块插入到链表中,其中包括空闲块合并的过程。
而realloc要做的是将一块已分配的块进行扩充N个字节,如果块尾部还有足够的内存空间,则将其增加到尾部。否则重新分配一块内存,并且把原先内存的内容复制到新开辟的内存中。根据heap4的内存管理策略,即判断在空闲块链表中是否包含源地址块尾部的地址,并且大小大于想要扩充的大小N,如有,则取出该空闲块,其中一部分作为源地址块的扩充内存,剩下的内存块组成新的空闲块,重新插入到链表中。否则,重新pvPortMalloc一块内存并复制源地址的内容,最后vPortFree源地址。
void *pvPortRealloc( uint8_t *srcaddr,size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;
BlockLink_t *pxBlockold,*pxBlockjudge;
vTaskSuspendAll();
{
/* 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();
}
/* 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 )
{
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
{
/* Byte alignment required. */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
{
if(srcaddr == NULL)
{
pvReturn = pvPortMalloc(xWantedSize);
( void ) xTaskResumeAll();
return pvReturn;
}
pxBlockold = (BlockLink_t *)(srcaddr - xHeapStructSize); //找到源地址对应的BlockLink_t结构体,提取其中的xBlockSize信息
pxBlockjudge = (BlockLink_t *)((uint8_t*)pxBlockold+((pxBlockold->xBlockSize)&(~xBlockAllocatedBit))); //找到源地址对应的块的尾部。
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while(pxBlock != pxBlockjudge&& ( pxBlock->pxNextFreeBlock != NULL )) //判断源地址块后的下一块是否可用
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if((xWantedSize< pxBlock->xBlockSize&&(( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE))&&pxBlock == pxBlockjudge) //源地址块的下一块地址可用,并且其块大小大于申请的大小
{
pxBlockold->xBlockSize += xWantedSize;
pxNewBlockLink = (BlockLink_t *)((uint8_t*)pxBlock + xWantedSize);
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
/* Insert the new block into the list of free blocks. */
prvInsertBlockIntoFreeList( pxNewBlockLink );
pxBlock->pxNextFreeBlock = NULL;
pvReturn = srcaddr;
xFreeBytesRemaining -= xWantedSize;
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
{
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
}
else
{
pvReturn = pvPortMalloc((((pxBlockold->xBlockSize)&(~xBlockAllocatedBit))-xHeapStructSize)+xWantedSize); //下一块不可用,重新申请一块地址空间,大小为原申请空间加上现在申请空间
memcpy((uint8_t*)pvReturn,srcaddr,(pxBlockold->xBlockSize&(~xBlockAllocatedBit)-xHeapStructSize));
vPortFree(srcaddr); //释放源地址空间
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
( void ) xTaskResumeAll();
configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
return pvReturn;
}