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

目录
  • 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的资料请关注我们其它相关文章!

(0)

相关推荐

  • FreeRTOS动态内存分配管理heap_5示例

    目录 heap_5.c vPortDefineHeapRegions 常见问题 heap_5.c heap5与heap4分配释放算法完全相同,只是heap5支持管理多块不连续的内存,本质是将多块不连续内存用链表串成一整块内存,再用heap4算法来分配释放.若使用heap5则在涉及到分配释放的函数调用时要先调用vPortDefineHeapRegions把多块不连续内存串成一块初始化. vPortDefineHeapRegions 此函数原型 void vPortDefineHeapRegions

  • FreeRTOS软件定时器apollo中断状态判断

    问题场景 开发中发现FreeRTOS软件定时器不走了,具体表现在软件定时器中断进不去. 分析问题 观察发现只有在某个任务执行期间,FreeRTOS的软件定时器才会不走,其他任务执行时正常,排查后是此任务的优先级比定时器任务高,且占用时间比较长,导致任务切不出去. 解决问题 在FreeRTOSConfig.h中修改定时器任务优先级为最高解决问题 apollo中断状态判断 在看apollo3 代码时发现下面这个函数 void WsfSetOsSpecificEvent(void) { if(xRad

  • FreeRTOS实时操作系统空闲任务的阻塞延时实现

    目录 什么是阻塞延时.为什么需要空闲任务 空闲任务的实现 阻塞延时的实现 xTicksToDelay 递减 SysTick初始化 仿真 什么是阻塞延时.为什么需要空闲任务 RTOS中的延时叫阻塞延时,即任务需要延时时,任务会放弃cpu使用权,cpu转而去做其他的事,当任务延时时间到后,任务重新请求获得cpu使用权.但当所有的任务都处于阻塞后,为了不让cpu空闲没事干就需要一个空闲任务让cpu干活. 空闲任务的实现 空闲任务实现和创建普通任务没区别,空闲任务在调用vTaskStartSchedul

  • FreeRTOS动态内存分配管理heap_4示例

    目录 heap_4.c 内存堆管理 数据结构如下 分配 内存堆初始化 把新构造的结构体插入空闲链表 释放 还剩空闲字节数 历史剩余最小字节数 适用范围.特点 heap_4.c 内存堆管理 heap_4也是用链表来管理,但是链表头用的是结构体,链表尾用的是指针,链表尾占用ucHeap内存 数据结构如下 /* Define the linked list structure. This is used to link free blocks in order of their memory addr

  • 使用FreeRTOS遇到死等异常的解决

    目录 问题场景: 追溯代码: 分析代码 问题场景: 在使用apollo3时,调试时发现在ADC中断中一发送信号量就卡住. 追溯代码: 追溯代码发现其实是在ADC中断中调用xQueueGenericSendFromISR就卡住,卡住位置如下 这个宏定义如下 继续往里看,发现卡在下面位置 此断言如下 所以打印看到的条件是0>=128,所以就while(1);卡在这里了 分析代码 这是获得ipsr寄存器的值,保存在ulCurrentInterrupt变量,那ipsr寄存器代表的是什么呢,这里有写htt

  • FreeRTOS实时操作系统的任务创建与任务切换

    目录 任务控制块数据结构 任务创建函数 定义就绪表 就绪表初始化 启动调度器 任务切换 任务控制块数据结构 任务控制块数据结构在task.c声明 typedef struct tskTaskControlBlock { volatile StackType_t * pxTopOfStack; //栈顶指针 ListItem_t xStateListItem; //任务节点 StackType_t * pxStack; //任务栈起始地址 char pcTaskName[configMAX_TAS

  • FreeRTOS实时操作系统的列表与列表项操作示例

    目录 前言 列表项数据结构 列表项初始化 列表数据结构 将列表项按照升序排列插入到列表 将列表项从列表删除 前言 FreeRTOS列表与列表项其实就是链表和节点,在list.c和list.h实现 列表项数据结构 //列表项数据结构 typedef struct xLIST_ITEM { TickType_t xItemValue; //辅助值,用作节点做顺序排序 struct xLIST_ITEM * pxNext;//后继指针 struct xLIST_ITEM * pxPrevious;//

  • FreeRTOS实时操作系统的多优先级实现

    目录 如何实现任务多优先级 软件通用方法和硬件指令方法 如何实现任务多优先级 FreeRTOS中,数字优先级越小,逻辑优先级也越小,空闲任务优先级为0.List_t pxReadyTasksLists[configMAX_PRIORITIES]是数组,数组下标代表任务优先级,任务创建是根据设置的任务优先级插入到对应下标的列表根节点上,如下. 要支持多优先级,就是再任务切换时让pxCurrentTCB指向最高优先级的TCB即可,之前时手动再任务1.任务2来回切换,现在问题就是怎么找到优先级最高的就

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

    目录 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.

  • FreeRTOS动态内存分配管理heap_1示例

    目录 动态内存管理 heap_1.c 内存堆管理 实际可用数组字节数 分配 释放 还剩空闲字节数 动态内存管理 FreeRTOS提供5种动态内存管理策略,分别为heap_1到heap_5,源码在FreeRTOS/Source/portable/MemMang下,本质是对一个或者多个大数组进行操作来对系统提供内存的申请.释放(有的策略没有)功能.下面先看看heap_1是怎么做的. heap_1.c 内存堆管理 大数组在哪里 /* Allocate the memory for the heap.

  • C语言编程C++动态内存分配示例讲解

    目录 动态内存管理 为什么存在动态内存分配 动态内存函数的介绍 malloc申请空间和free释放空间 有借有还 free释放内存 calloc申请内存 realloc调整动态内存的大小 realloc使用的注意事项 当然realloc也可以直接开辟空间 常见的动态内存错误 1.对NULL指针的解引用操作 2.对动态开辟空间的越界访问 3.对非动态开辟内存使用free释放 4.使用free释放一块动态内存开辟的一部分 5.对同一块动态内存多次释放 6.动态开辟内存忘记释放(内存泄漏) 几个面试题

  • C语言编程动态内存分配常见错误全面分析

    目录 前言:为什么存在动态内存分配? 一.动态内存函数 1.malloc和free函数 2.calloc函数 3.realloc函数 二.常见错误 1.对NULL指针解引用 2.对动态开辟空间的越界访问 3.对非动态开辟使用free函数 4.使用free释放一块动态内存开辟内存的一部分 5.对同一块空间多次释放 6.动态开辟内存忘记释放 总结 前言:为什么存在动态内存分配? 我们已经掌握的内存开辟方式如下 int a=10;//在栈空间上开辟4字节 char arr[10]={0};//在栈空间

  • C语言动态内存分配的详解

    C语言动态内存分配的详解 1.为什么使用动态内存分配 数组在使用的时候可能造成内存浪费,使用动态内存分配可以解决这个问题. 2. malloc和free C函数库提供了两个函数,malloc和free,分别用于执行动态内存分配和释放. (1)void *malloc(size_t size); malloc的参数就是需要分配的内存字节数.malloc分配一块连续的内存.如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针. (2)void free(void *poi

  • 动态内存分配导致影响Javascript性能的问题

    内存分配对性能的影响是很大的,分配内存本身需要时间,垃圾回收器回收内存也需要时间,所以应该尽量避免在堆里分配内存.不过直到最近优化HoLa cantk时,我才深刻的体会到内存分配对性能的影响,其中有一个关于arguments的问题挺有意思,写在这里和大家分享一下. 我要做的事情是用webgl实现canvas的2d API(这个话题本身也是挺有意思的,有空我们再讨论),drawImage是一个重要的函数,游戏会频繁的调用它,所以它的性能至关重要.drawImage的参数个数是可变的,它有三种形式:

  • C语言动态内存分配函数的实现

    在C中我们开辟内存空间有两种方式 : 1.静态开辟内存 :例如: int a;int b[10]; 这种开辟内存空间的特点是 所开辟的内存是在栈中开辟的固定大小的 ,如a是4字节 ,数组b是40字节 ,并且数组在申明时必须指定其长度 , 如果是全局数组的话,内存是在编译时分配好的,如果是局部变量数组的话,运行时在栈上静态分配内存.不管是全局数组还是局部数组,它们都有一个特点,那就是数组大小是确定的,是代码中写死的.那如果我们想在程序运行时才确定一个数组的大小 , 前两种在栈上分配内存的方法显然是

  • Android NDK开发(C语言--动态内存分配)

    1.C 内存管理函数 C 语言为内存的分配和管理提供了几个函数.这些函数可以在 <stdlib.h> 头文件中找到. 序号 函数和描述 1 void calloc(int num, int size); 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0.所以它的结果是分配了 numsize 个字节长度的内存空间,并且每个字节的值都是0. 2 void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的

随机推荐