FreeRTOS动态内存管理(heap_4)

heap_4.c

内存堆管理

heap_4也是用链表来管理,但是链表头用的是结构体,链表尾用的是指针,链表尾占用ucHeap内存
数据结构如下

/* 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;

头尾链表如下,注意pxEnd是指针

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

分配
void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
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. */
		//pxEnd是NULL则是第一次调用,需要初始化堆
		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. */
		//xBlockAllocatedBit = 0x8000_0000;
		//待分配的内存不能大于0x7FFF_FFFF,否则失败
		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. */
				//xWantedSize大小进行字节对齐调整
				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();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
            //xWantedSize大于0且小于等于此时还剩字节数才能往下申请
			if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
			{
				/* Traverse the list from the start	(lowest address) block until
				one	of adequate size is found. */
				//pxPreviousBlock指向头链表
				pxPreviousBlock = &xStart;
				//pxBlock指向头链表的下一个即第一个空闲块
				pxBlock = xStart.pxNextFreeBlock;
				//开始遍历找到第一个比xWantedSize大的空闲块
				while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
				{
				    //pxPreviousBlock保存空闲块的上一个
					pxPreviousBlock = pxBlock;
					pxBlock = pxBlock->pxNextFreeBlock;
				}

				/* If the end marker was reached then a block of adequate size
				was	not found. */
				//遍历完成pxBlock != pxEnd说明找到符合的空闲块
				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指向的空闲块被分配了,
					//所以要把pxPreviousBlock->pxNextFreeBlock指向的空闲块移除出去,
					//也就是pxPreviousBlock->pxNextFreeBlock指向pxBlock->pxNextFreeBlock
					//也就是跳过分配出去的那个块
					pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;

					/* If the block is larger than required it can be split into
					two. */
					//这里判断,
					//如果将要分配出去的内存块大小xBlockSize比分配出去的还要大heapMINIMUM_BLOCK_SIZE(2倍管理结构体)
					//为了节约就把再分成2块,一块返回给用户,
					//一块构造一个新的空闲管理结构体后插入空闲链表
					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. */
						//注意这里xWantedSize是管理结构体和和真正需要字节数之和
						//所以是在pxBlock基础上偏移xWantedSize作为新的管理结构体
						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新的管理结构体大小
						//是待分配pxBlock->xBlockSize-xWantedSize
						pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
						//更新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. */
					//xBlockSize最高位置1表示被这块内存被分配出去
					pxBlock->xBlockSize |= xBlockAllocatedBit;
					//所以管理结构体的next要指向NULL
					pxBlock->pxNextFreeBlock = NULL;
				}
				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 )
		{
			extern void vApplicationMallocFailedHook( void );
			vApplicationMallocFailedHook();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif
//返回给用户
	configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
	return pvReturn;
}

内存堆初始化
static void prvHeapInit( void )
{
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表示管理的总内存字节数
		xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
	}
    //pucAlignedHeap指向对齐后首址
	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此时指向管理内存最后
	uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
	//退回一个BlockLink_t(字节对齐后)大小字节数
	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;
	//第一个空闲块字节数=uxAddress(此时值=pxEnd) - pxFirstFreeBlock(此时值=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. */
	//这里sizeof( size_t ) = 4,heapBITS_PER_BYTE=8,表示1字节有8bit
	//xBlockAllocatedBit = 1<<(4*8-1) = 0x8000_0000;
	//FreeRTOS用xBlockSize最高位来标记此内存块是否空闲
	//所以heap4最大只能管理0x7FFF_FFFF字节内存
	xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}

初始化后的示意图如下
注意xEnd结构体占用的时堆内存
在这里插入图片描述

把新构造的结构体插入空闲链表
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
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. */
	//这里是根据内存块的地址大小来迭代寻找和pxBlockToInsert相邻的前一个空闲的内存块
	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? */
	//这里判断pxBlockToInsert是否能与pxBlockToInsert相邻的前一个空闲的内存块合并
	puc = ( uint8_t * ) pxIterator;
	if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
	{   //这里做向前合并,xBlockSize相加
		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;
	if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
	{   //这里做向后合并,如果要合并的后向不是pxEnd
		if( pxIterator->pxNextFreeBlock != pxEnd )
		{  //这里把后项合入到pxBlockToInsert
			/* Form one big block from the two blocks. */
			pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
			//pxBlockToInsert的下一个指向后项指向的空闲块
			pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
		}
		else//如果后项是pxEnd就不能合并,指向pxEnd
		{
			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();
	}
}

释放
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. */
		//检查这个内存块是否是heap4之前分配的
		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. */
				//把分配的xBlockSize最高位标记清除
				pxLink->xBlockSize &= ~xBlockAllocatedBit;
                //挂起调度器
				vTaskSuspendAll();
				{
					/* Add this block to the list of free blocks. */
				   //更新剩余内存数
					xFreeBytesRemaining += pxLink->xBlockSize;
					traceFREE( pv, pxLink->xBlockSize );
					//插入空闲内存链表
					prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
				}//解挂调度器
				( void ) xTaskResumeAll();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
}

还剩空闲字节数
size_t xPortGetFreeHeapSize( void )
{
	return xFreeBytesRemaining;
}
历史剩余最小字节数
size_t xPortGetMinimumEverFreeHeapSize( void )
{
	return xMinimumEverFreeBytesRemaining;
}
适用范围、特点

heap4在heap2基础上加入了合并内存碎片算法,把相邻的内存碎片合并成一个更大的块、且xEnd结构体占用的是内存堆空间。
heap2的管理结构体链表是按照xBlockSize大小升序串起来,所以空闲块插入也是按照空闲块大小升序插入,而heap4管理结构体是按照空闲块管理结构体地址大小升序串起来,这样做是为了判断地址是否连续,若连续则能进行碎片合并,且用xBlockSize的最高为标记是否是已经分配的。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS 中进行动态内存申请,可以使用 FreeRTOS 内置的内存管理函数。FreeRTOS 提供了两种内存管理方案:heap_1 和 heap_5。 1. heap_1:heap_1 是最简单的内存管理方案,它使用 C 标准库中的 malloc() 和 free() 函数来实现内存的分配和释放。你可以通过在 FreeRTOSConfig.h 文件中定义 configUSE_MALLOC 宏来启用 heap_1 内存管理方案。 2. heap_5:heap_5 是更高级的内存管理方案,它使用 FreeRTOS 内置的内存管理函数 pvPortMalloc() 和 vPortFree() 来实现内存的分配和释放。heap_5 允许你通过定义 configAPPLICATION_ALLOCATED_HEAP 宏来指定一个连续的内存区域供 FreeRTOS 作为堆内存使用。 以下是在 FreeRTOS 中进行动态内存申请的示例代码: ```c #include "FreeRTOS.h" #include "task.h" void vTaskFunction(void *pvParameters) { // 动态分配内存 int *ptr = (int *)pvPortMalloc(sizeof(int)); if (ptr != NULL) { // 内存分配成功,可以进行操作 *ptr = 123; // ... // 释放内存 vPortFree(ptr); } vTaskDelete(NULL); } int main(void) { // 创建任务 xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL); // 启动调度器 vTaskStartScheduler(); return 0; } ``` 在上述示例中,任务函数 vTaskFunction() 中使用 pvPortMalloc() 函数动态分配了一个 int 类型的内存,并在使用完后通过 vPortFree() 函数释放内存。请注意,内存的释放要确保在不再使用时执行,以避免内存泄漏。 需要注意的是,动态内存分配是一项复杂的任务,可能涉及到内存碎片、多任务竞争等问题。因此,在使用动态内存时,需谨慎规划和管理,以确保系统的可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值