信息发布→ 登录 注册 退出

FreeRTOS动态内存分配管理heap_2示例

发布时间:2026-01-11

点击量:
目录
  • heap_2.c
    • 内存堆管理
  • 分配
    • 初始化内存堆
      • 把新构造的结构体插入空闲链表
        • 释放
          • 还剩空闲字节数
            • 适用范围、特点

              heap_2.c

              内存堆管理

              heap_2和heap_1一样是开辟一个大数组作为堆空间供用户使用,但是采用单项不循环链表来管理内存的分配释放,主要思想是用链表把内存块串起来,数据结构如下

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

              与引入链表管理而带来的相关变量如下

              //链表结构体对齐后所占字节数
              static const uint16_t heapSTRUCT_SIZE	= ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
              //2倍链表结构体对齐后所占字节数,这作为一个阈值,在分配时起作用
              #define heapMINIMUM_BLOCK_SIZE	( ( size_t ) ( heapSTRUCT_SIZE * 2 ) )
              /* Create a couple of list links to mark the start and end of the list. */
              //定义2个局部静态全局结构体变量用于管理
              static BlockLink_t xStart, xEnd;
              

              还剩空闲字节数

              /* Keeps track of the number of free bytes remaining, but says nothing about
              fragmentation. */
              static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
              

              分配

              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 is increased so it can contain a BlockLink_t
              		structure in addition to the requested amount of bytes. */
              		if( xWantedSize > 0 )
              		{
              		    //用户分配字节数+管理结构体占用字节数
              			xWantedSize += heapSTRUCT_SIZE;
              
              			/* Ensure that blocks are always aligned to the required number of bytes. */
              			//总的字节数再做此字节对齐
              			if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 )
              			{
              				/* Byte alignment required. */
              				xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
              			}
              		}
                      //待分配字节数大于0且小于总共堆字节数
              		if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) )
              		{
              			/* Blocks are stored in byte order - traverse the list from the start
              			(smallest) block until one of adequate size is found. */
              			//pxPreviousBlock指向头链表
              			pxPreviousBlock = &xStart;
              			//pxBlock指向第一个开始空闲块
              			pxBlock = xStart.pxNextFreeBlock;
              			//当pxBlock所管理的空闲块字节数小于待分配的
              			//且没有遍历到空闲块管理链表尾部则一直遍历
              			while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
              			{
              				//pxPreviousBlock这里是保存当前空闲块管理结构体,为了后面找到返回的内存地址
              				pxPreviousBlock = pxBlock;
              				//指向下一个空闲块管理结构体
              				pxBlock = pxBlock->pxNextFreeBlock;
              			}
              			/* If we found the end marker then a block of adequate size was not found. */
              			//pxBlock不等于结尾说明找到符合大小的空闲块
              			if( pxBlock != &xEnd )
              			{
              				/* Return the memory space - jumping over the BlockLink_t structure
              				at its start. */
              				//pvReturn用作返回给用户,这里要偏移一个空闲块管理结构体占用内存大小
              				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指向的空闲块被分配了,
              				//所以要把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 );
              
              					/* 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. */
              					//把新构造的空闲管理结构体按xBlockSize大小升序插入到空闲链表
              					prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
              				}
                              //还剩空闲字节数要减去分配出去的
              				xFreeBytesRemaining -= pxBlock->xBlockSize;
              			}
              		}
              
              		traceMALLOC( pvReturn, xWantedSize );
              	}//解挂调度器
              	( void ) xTaskResumeAll();
              //如果定义了申请失败钩子函数,这里将执行
              	#if( configUSE_MALLOC_FAILED_HOOK == 1 )
              	{
              		if( pvReturn == NULL )
              		{
              			extern void vApplicationMallocFailedHook( void );
              			vApplicationMallocFailedHook();
              		}
              	}
              	#endif
              //返回给用户
              	return pvReturn;
              }

              其中xFreeBytesRemaining初始化如下

              /* Keeps track of the number of free bytes remaining, but says nothing about
              fragmentation. */
              static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
              

              初始化内存堆

              static void prvHeapInit( void )
              {
              BlockLink_t *pxFirstFreeBlock;
              uint8_t *pucAlignedHeap;
              	/* Ensure the heap starts on a correctly aligned boundary. */
                  //与heap1操作相同,确保portBYTE_ALIGNMENT字节对齐,实际使用的首址是pucAlignedHeap
              	pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( 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. */
              	//空闲链表结构体头部初始化,pxNextFreeBlock指向实际使用的首址pucAlignedHeap
              	xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
              	//空闲链表结构体头部没有可用内存,所以xBlockSize是0
              	xStart.xBlockSize = ( size_t ) 0;
              	/* xEnd is used to mark the end of the list of free blocks. */
              	//空闲链表结构体尾部初始化,xBlockSize=configADJUSTED_HEAP_SIZE仅仅是为了后面的升序排列,不代表可以空闲字节数
              	xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
              	//空闲链表结构体尾部初始化,pxNextFreeBlock指向NULL表示结尾
              	xEnd.pxNextFreeBlock = NULL;
              	/* To start with there is a single free block that is sized to take up the
              	entire heap space. */
              	//第一个空闲块,pxFirstFreeBlock,即上面xStart指向的pucAlignedHeap
              	pxFirstFreeBlock = ( void * ) pucAlignedHeap;
              	//可以空闲内存为configADJUSTED_HEAP_SIZE
              	pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
              	//指向空闲链表结构体尾部
              	pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
              }
              
              

              初始化后的示意图如下
              这里注意xBlockSize是包括管理结构体占用内存大小的(出来xStart和xEnd之外,这2个做排序用)

              把新构造的结构体插入空闲链表

              /* STATIC FUNCTIONS ARE DEFINED AS MACROS TO MINIMIZE THE FUNCTION CALL DEPTH. */
              /*
               * Insert a block into the list of free blocks - which is ordered by size of
               * the block.  Small blocks at the start of the list and large blocks at the end
               * of the list.
               */
              #define prvInsertBlockIntoFreeList( pxBlockToInsert )								\
              {																					\
              BlockLink_t *pxIterator;															\
              size_t xBlockSize;																	\																					\
                  //这里获得新构造的空闲结构体成员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;									\
              }
              
              

              释放

              释放就很简单了,就是偏移下地址后直接插入空闲链表

              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();
              	}
              }

              还剩空闲字节数

              size_t xPortGetFreeHeapSize( void )
              {
              	return xFreeBytesRemaining;
              }
              

              适用范围、特点

              适用于需要释放的场合,且每次申请释放的内存都是固定大小的,因为释放时不会合并相邻空闲内存块,所以如果每次申请释放都是随机的,到最后即使剩余内存大于要想要分配,由于有很多小的内存碎片导致最终分配失败。

              以上就是FreeRTOS动态内存分配管理heap_2示例的详细内容,更多关于FreeRTOS动态内存管理heap_2的资料请关注其它相关文章!

              在线客服
              服务热线

              服务热线

              4008888355

              微信咨询
              二维码
              返回顶部
              ×二维码

              截屏,微信识别二维码

              打开微信

              微信号已复制,请打开微信添加咨询详情!