FreeRTOS内存管理

内存管理是FreeRTOS非常重要的一项功能,前面章节所讲述的任务、消息队列、信号量、事件标志组及软件定时器等在创建时都有两种方法:一种是动态内存分配方法;另种是由用户指定内存的静态方法。使用动态内存分配方法所需要的内存就是从FreeRTOS所管理的内存区域进行分配的。

1 FreeRTOS内存分配方法

FreeRTOS支持5种动态内存管理方法,分别通过文件heap_1、heap_2、heap_3、heap_4
和heap_5实现,在实际使用时,只需使用其中的一种即可。除heap_3动态内存管理之外,FreeRTOS均通过内存堆ucHeap[]来管理内存,内存堆的大小为由宏configTOTAL_HEAP_SIZE所设定的大小。

#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
	//如果由用户管理内存,则需要由用户实现内存分配
	extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
	static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */

 在程序运行过程中,可用xPortGetFreeHeapSize()函数获取动态剩余内存堆大小。

1.1 heap_1.c动态内存管理方法

heap_1.c 动态内存管理方法是5种动态内存管理方法中最简单的一种,采用这种方法,动态内存一旦申请就不允许释放。尽管如此,这种方法还是能满足大部分嵌入式应用的要求。这种嵌入式应用在系统启动阶段就完成了任务创建,以及消息队列、信号量、事件标志组和软件定时器的创建,而且这些资源在系统运行中是一直要使用的,所以也就不需要删除以进行内存释放。

heap_1动态内存管理方法有如下特性。
(1)项目应用不需要删除任务、信号量、消息队列等已经创建好的资源。
(2)具有时间确定性,即申请动态内存的时间是固定的,并且不会产生内存碎片。
(3)代码实现和内存分配过程非常简单。

1.2 heap_2.c动态内存管理方法

与heap_1动态内存管理方法不同,heap2动态内存管理方法利用了最适应算法,并且支持内存释放。但是heap_2动态内存管理方法不支持内存碎片整理,随着内存的不断申请和释放,会出现大量的内存碎片——小块碎片化的内存,最后可能导致无内存可用。heap_2动态内存管理方法有如下特性。

(1)在不考虑内存碎片的情况下,heap_2.c动态内存管理方法支持重复的任务、信号量、事件标志组、软件定时器等内部资源的创建和删除。
(2)如果用户申请和释放的动态内存大小是随机、可变的,则不建议采用heap_2.c动态内存管理方法,因为采用这种方法容易产生内存碎片。
(3)如果用户需要随机创建和删除任务、消息队列、事件标志组、信号量等内部资源,也不建议采用heap_2.c 动态内存管理方法,因为采用这种方法容易产生内存碎片。
(4)不具有时间确定性,但是比C库中的malloc)函数效率高。

 1.3 heap_3.c动态内存管理方法

heap_3.c 动态内存管理方法实现的动态内存管理是对编译器提供的malloc()函数和free()函数进行简单封装,额外做了线程保护。
heap_3动态内存管理方法有如下特性。

(1)需要编译器提供malloc()函数和free()函数,内存堆大小不由宏configTOTAL_HEAP_SIZE所决定,而由启动文件设置。STM32微控制器所使用的由启动文件设置内存堆大小所决定。

(2)不具有时间确定性,即申请动态内存的时间不是固定的。
(3)可能会增加FreeRTOS内核的代码量。

  1.4 heap_4.c动态内存管理方法

与heap_2动态内存管理方法不同,heap_4动态内存管理方法使用了最优匹配算法,
并且支持内存碎片的回收,能将零散的内存碎片整理为一个大的内存块。
heap_4动态内存管理方法有如下特性。

(1)可以用于需要重复地创建和删除任务、信号量、事件标志组、软件定时器等内部
资源的场合。
(2)即使随机地调用pvPortMalloc()函数和vPortFree()函数,并且每次申请的内存大
小都不同,也不会像heap_2动态内存管理方法一样产生很多内存碎片。
(3)不具有时间确定性,即申请动态内存的时间不是固定的,但是比C库中的malloc()函数效率高。
基于以上特性,本书FreeRTOS移植、配套使用的例子,都使用了heap_4动态内存管理方法。

   1.5 heap_5.c动态内存管理方法

heap_5动态内存管理方法使用了和heap_4动态内存管理方法相同的合并算法,内存管理实现也基本相同,但heap_5动态内存管理方法允许内存堆跨越多个不连续的内存段。另外,使用heap_5动态内存管理方法,要先通过vPortDefineHeapRegions()函数进行初始化,也就是说用户在创建任务等操作FreeRTOS 的内部资源前,要先调用vPortDefineHeapRegions()函数,否则无法通过 pvPortMalloc()函数申请到动态内存。
heap_5动态内存管理方法有如下特性。

  (1)具有与heap_4动态内存管理方法一样的一些特性,如支持碎片回收等。

(2)能跨区域管理内存。
(3)使用稍显复杂。

 2 FreeRTOS内存管理示例

本示例改写自任务运行时间信息示例,保存任务运行信息的内存区采用动态内存分配方法进行分配,增加检测FreeRTOS可用内存堆大小功能,其余代码与原示例基本相同。

增加一个按键KEY3,用于为获取到的任务信息动态申请内存空间,原Key1按键和Key2按键功能不变。

2.1 FreeRTOS内存堆

分配60kb的内存用于FreeRTOS任务创建,等消息队列,信号量,时间标志组和软件定时器创建等动态内存分配。

/*堆内存大小:供 FreeRTOS使用的总堆内存大小*/
#define configTOTAL_HEAP_SIZE		                        ( ( size_t ) ( 60 * 1024 ) )

2.2 任务函数

#include "stm32f10x.h"                  // Device header
#include "appTask.h"
#include "LED.h"
#include "Delay.h"
#include "Serial.h"
#include "Key.h"


static TaskHandle_t Led0TaskHandle = NULL;//任务LED0任务句柄
static TaskHandle_t Led1TaskHandle = NULL;//任务LED1任务句柄
static TaskHandle_t tskInfoTaskHandle = NULL;//任务getTaskInfo任务句柄
/**********************************************************************
函 数 名:Led0Task
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
优 先 级:	3
**********************************************************************/
static void Led0Task(void *pvParameters)
{
	uint32_t testArray[128] ;						//任务中的局部变量

	while(1)
	{
		
		GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));	//LED0闪烁
		
		vTaskDelay(pdMS_TO_TICKS(500));//每秒闪1次
		
		
	}
}
/**********************************************************************
函 数 名:Led1Task

形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
优 先 级:	2
**********************************************************************/
static void Led1Task(void *pvParameters)
{
	uint32_t testArray[128] = {0};						//任务中的局部变量

	
	while(1)
	{
		
		
		GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5)));  //LED1闪烁
		
		vTaskDelay(pdMS_TO_TICKS(250));//每秒闪2次
	
	}	
}
/**********************************************************************
函 数 名:getTaskInfo
功能说明:动态内存申请和释放,获取任务状态信息,并通过串口发送
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
优 先 级:	4
**********************************************************************/
void getTaskInfo(void *pvParameters)
{
	uint8_t ucKeyValue = 0;  //保存键值
	uint32_t uHeapSize; //保存剩余内存堆的大小
	char *pcTaskINfo = NULL; //指向保存任务状态信息的内存区
	
	while(1)
	{
		ucKeyValue = Key_getNum();
		if(ucKeyValue == 1)
		{
			if(pcTaskINfo != NULL)  //申请了动态内存
			{
				vTaskList(pcTaskINfo);
				printf("任务名 任务状态 优先级 剩余堆栈大小 任务号 \r\n");
				printf("%s\r\n",pcTaskINfo); 
				vPortFree(pcTaskINfo);  //释放内存
				pcTaskINfo = NULL ;
			}else
			{
					printf("请先通过按键1申请内存! \r\n");
			}
		}else if(ucKeyValue == 2)
		{
			if(pcTaskINfo != NULL)  //申请了动态内存
			{
				vTaskGetRunTimeStats(pcTaskINfo);
				printf("任务名\t\t运行时间\t\t百分比 \r\n");
				printf("%s\r\n",pcTaskINfo); 
				vPortFree(pcTaskINfo);  //释放内存
				pcTaskINfo = NULL ;
			}else
			{
					printf("请先通过按键1申请内存! \r\n");
			}
		}else if(ucKeyValue == 3)
		{
			if(pcTaskINfo != NULL)  //已申请了动态内存
			{
				vPortFree(pcTaskINfo); 	 //释放内存
				pcTaskINfo = NULL;
			}
			uHeapSize = xPortGetFreeHeapSize();  //获取剩余内存堆大小
			printf("Key1申请内存,内存堆剩余%8d字节\r\n",uHeapSize);
			pcTaskINfo= pvPortMalloc(1024);  //通过heap_4 动态内存管理方法申请内存
			
			if(pcTaskINfo != NULL)
			{
				uHeapSize = xPortGetFreeHeapSize(); //获取剩余内存堆大小
				printf("Key1申请内存,内存堆剩余%8d字节\r\n",uHeapSize);
				printf("动态内存地址:%x \r\n\r\n",(uint32_t)pcTaskINfo);
			}else
			{
				printf("动态内存申请失败!\r\n");
			}
		}
		
		vTaskDelay(pdMS_TO_TICKS(100)); //阻塞100ms
	}
}
/**********************************************************************
函 数 名:appStartTask
功能说明:任务开始函数,用于创建其他函数并且开启调度器
形    参:pvParameters 是在创建该任务时传递的参数
返 回 值:无
**********************************************************************/
void appStartTask(void)
{
		taskENTER_CRITICAL();   /*进入临界段,关中断*/
		xTaskCreate(Led0Task,"Led0Task",256,NULL,3,&Led0TaskHandle);
		xTaskCreate(Led1Task,"Led1Task",256,NULL,2,&Led1TaskHandle);
		xTaskCreate(getTaskInfo,"getTaskInfo",512,NULL,4,&tskInfoTaskHandle);
		taskEXIT_CRITICAL(); 	/*退出临界段,关中断*/
		vTaskStartScheduler();/*开启调度器*/
}

2.3 下载测试

本示例能获取到比较详细的FreeRTOS内存堆大小、任务堆栈、任务运行时间等信息对于系统设计是非常有用的。FreeRTOS 需要多大的内存堆,需要多大的任务堆栈,这些信息很难一开始就计算准确,可以通过本示例的办法,在系统设计、调试阶段测试这些信息,然后逐步调整,注意一定要让内存堆及任务堆栈有足够的余量,以免引起堆栈溢出,造成系统崩溃。

3 总结

FreeRTOS 提供了5种动态内存管理方法。heap_1动态内存管理方法是5种方法中最简单的,但是申请的内存不允许释放。heap_2动态内存管理方法支持动态内存的申请和释放,但是不支持内存碎片的整理。heap_3动态内存管理方法采用编译器自带的malloc()函数和firee()函数进行简单的封装,以支持线程安全,即支持多任务调用。heap_4动态内存管理方法支持动态内存的申请和释放,支持内存碎片整理。heap_5动态内存管理方法在heap_4动态内存管理方法的基础上支持将动态内存设置在不连续的区域上。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值