UCOSII-内存管理

一.前言

作为一个RTOS操作系统,内存管理是必备的功能,因此UCOSII也具有内存管理能力。通常应用程序可以调用ANSIC编译器的malloc()和free()函数来动态的分配和释放内存,但是嵌入式实时操作系统中最好不要这么做,多次这样的操作会把原来很大的一块连续存储区逐渐分割成许多非常小并且彼此不相邻的存储区域,这就是存储碎片。
UCOSII中将存储空间分成,每个存储区有数量不等大小相同的存储块,在一个系统中可以有多个存储区。

二.内存控制块

操作系统以分区为单位来管理动态内存,而任务以内存块为单位来获得和释放动态内存。内存分区及内存块的使用情况则由内存控制块来记录。

1.内存分区及内存控制块的定义

在内存中定义一个内存分区及其内存块的方法非常简单,只需定义一个二维数组即可。例如:

INT8U IntMemBuf[10][10];   //定义一个内存区,包含10个内存块,每个内存块由10个u8数据构成

2.内存控制块OS_MEM的结构

#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
typedef struct os_mem {                   /* MEMORY CONTROL BLOCK                                      */
    void   *OSMemAddr;                    /* 内存分区的指针                  */
    void   *OSMemFreeList;                /* 内存控制块链表指针                     */
    INT32U  OSMemBlkSize;                 /* 内存块的长度                      */
    INT32U  OSMemNBlks;                   /* 分区内内存块的数目                 */
    INT32U  OSMemNFree;                   /* 分区内当前可分配的内存块的数目       */
#if OS_MEM_NAME_EN > 0u
    INT8U  *OSMemName;                    /* Memory partition name                                     */
#endif
} OS_MEM;

内存控制块与内存分区和内存块之间的关系:
在这里插入图片描述

3.空内存控制块链表

在UCOSII初始化时,会调用内存控制块的初始化函数OS_MemInit()定义并初始化一个空内存控制块链表
在这个空内存控制块链表中,一个有OS_MAX_MEM_PART(在文件OS_CFG.H中定义的常数)个空内存控制块。这些空内存控制块的指针成员OSMemFreeList暂时作为指向下一个空内存控制块指针。
空内存控制块链表的结构如下图所示:
在这里插入图片描述
每当应用程序需要创建一个内存分区时,系统就会从空内存控制块链表中摘取一个控制块,而把链表的头指针OSMemFreeList指向下一个空内存控制块;而每当应用程序释放一个内存分区时,则会把该分区对应的内存控制块归还给空内存控制块链表。

三.动态内存的管理

动态内存管理函数有:
创建动态内存分区函数OSMemCreate();

OS_MEM  *OSMemCreate (void   *addr,    //内存分区的起始地址
                      INT32U  nblks,   //分区中内存块的数目
                      INT32U  blksize, //每个内存块的字节数
                      INT8U  *perr)    //错误信息

请求获得内存块函数OSMemGet();

void  *OSMemGet (OS_MEM  *pmem,     //内存分区的指针
                 INT8U   *perr)     //错误信息

释放内存块函数OSMemPut();

INT8U  OSMemPut (OS_MEM  *pmem,    //内存分区的指针
                 void    *pblk)    //待释放内存块的指针

查询动态内存分区状态函数OSMemQuery();

#if OS_MEM_QUERY_EN > 0u
INT8U OSMemQuery  (OS_MEM    *pmem,  //待查询的内存控制块的指针
         OS_MEM_DATA  *p_mem_data); //存放分区状态信息的结构的指针

其中p_mem_data是一个OS_MEM_DATA 类型的结构。定义如下:

typedef struct os_mem_data {
    void   *OSAddr;                    /* 内存分区的指针     */
    void   *OSFreeList;                /* 分区内内存块链表的头指针   */
    INT32U  OSBlkSize;                 /* 内存块的长度             */
    INT32U  OSNBlks;                   /* 分区内内存块的数目                    */
    INT32U  OSNFree;                   /* 分区内空闲内存块的数目                             */
    INT32U  OSNUsed;                   /* 已被分配的内存块数目                */
} OS_MEM_DATA;

四.内存管理应用代码(基于stm32f4)

/**********UCOSII任务堆栈设置*****************/
//1.START 任务
//设置任务优先级
#define START_TASK_PRIO      			10 //开始任务的优先级设置为最低
//设置任务堆栈大小
#define START_STK_SIZE  				128
//创建任务堆栈空间	
OS_STK START_TASK_STK[START_STK_SIZE];
//任务函数接口
void start_task(void *pdata);

//2.main_task主任务
//任务优先级
#define MAIN_TASK_PRIO		4
//任务堆栈大小	
#define MAIN_STK_SIZE 		128
//任务堆栈	
OS_STK MAIN_TASK_STK[MAIN_STK_SIZE];
//任务函数
void main_task(void *pdata);

//3.memmanage_task内存管理任务
//任务优先级
#define MEMMANAGE_TASK_PRIO	5
//任务堆栈大小
#define MEMMANAGE_STK_SIZE	128
//任务堆栈
OS_STK MEMMANAGE_TASK_STK[MEMMANAGE_STK_SIZE];
//任务函数
void memmanage_task(void *pdata);

/********************定义存储区**********************/
//定义一个存储区(内部)
OS_MEM *INTERNAL_MEM;	                  //定义内存控制块指针
//存储区中存储块数量
#define INTERNAL_MEM_NUM		5
//每个存储块大小
//由于一个指针变量占用4字节所以块的大小一定要为4的倍数
//而且必须大于一个指针变量(4字节)占用的空间,否则的话存储块创建不成功
#define INTERNAL_MEMBLOCK_SIZE	100  
//存储区的内存池,使用内部RAM
u8 Internal_RamMemp[INTERNAL_MEM_NUM][INTERNAL_MEMBLOCK_SIZE]; //划分分区及内存块

//定义一个存储区(外部)
OS_MEM *EXTERNAL_MEM;	                   //定义内存控制块指针
//存储区中存储块数量
#define EXTRENNAL_MEM_NUM		5
//每个存储块大小
//由于一个指针变量占用4字节所以块的大小一定要为4的倍数
//而且必须大于一个指针变量(4字节)占用的空间,否则的话存储块创建不成功
#define EXTERNAL_MEMBLOCK_SIZE	100
//存储区的内存池,使用外部SRAM(位置:0X68000000)
u8 External_RamMemp[EXTRENNAL_MEM_NUM][EXTERNAL_MEMBLOCK_SIZE]  __attribute__((at(0X68000000)));//划分分区及内存块
/********************main主函数**********************/
int main(void)
{ 
	delay_init(168);		//延时初始化 
	uart_init(115200);	    //串口初始化波特率为115200
	LED_Init();		  		//初始化与LED连接的硬件接口  
	KEY_Init();             //key初始化
	LCD_Init();			    //LCD初始化	
	FSMC_SRAM_Init();	    //初始化SRAM
	ucos_load_main_ui();    //加载主UI1
    OSInit();  	 			//初始化UCOSII		 		
 	OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
	OSStart();	      //开始执行UCOS程序
}

//开始任务
void start_task(void *pdata)
{
	u8 err;
    OS_CPU_SR cpu_sr=0;
	pdata = pdata; 		
	OSStatInit();					          //初始化统计任务.这里会延时1秒钟左右	
 	OS_ENTER_CRITICAL();			      //进入临界区(无法被中断打断)    
    //创建一个内部存储分区
	INTERNAL_MEM=OSMemCreate(Internal_RamMemp,INTERNAL_MEM_NUM,INTERNAL_MEMBLOCK_SIZE,&err);
	//创建一个外部存储分区,使用外部SRAM(位置:0X68000000)
	EXTERNAL_MEM=OSMemCreate(External_RamMemp,EXTRENNAL_MEM_NUM,EXTERNAL_MEMBLOCK_SIZE,&err);  
    OSTaskCreate(main_task,(void *)0,(OS_STK*)&MAIN_TASK_STK[MAIN_STK_SIZE-1],MAIN_TASK_PRIO);	         //在开始任务中创建main任务	 					
	OSTaskCreate(memmanage_task,(void *)0,(OS_STK*)&MEMMANAGE_TASK_STK[MEMMANAGE_STK_SIZE-1],MEMMANAGE_TASK_PRIO);	         //在开始任务中创建memmanage_task任务	 					
	OSTaskSuspend(START_TASK_PRIO);	      //挂起起始任务.
	OS_EXIT_CRITICAL();				      //退出临界区(可以被中断打断)
}	

//主任务的任务函数(完成内存的申请及释放)
void main_task(void *p_arg)
{
	u8 key,num,i=0;
	static u8 external_memget_num;
	u8 *internal_buf[5];
	u8 *external_buf;
	u8 err;
	while(1)
	{
		key = KEY_Scan(0);         //扫描按键
		switch(key)
		{
			case WKUP_PRES:		       //按下KEY_UP键:申请内部内存
			for(i=0;i<5;i++)
			{
			   internal_buf[i]=OSMemGet(INTERNAL_MEM,&err);     //循环申请五次内存并放在internal_buf[i]缓存区内
				 if(err == OS_ERR_NONE) //内存申请成功
			   {
					  sprintf((char*)internal_buf[i],"internal_buf[%d]=%d\r\n",i,i);//在申请到的内存区域内放置数据
					  printf("%s",internal_buf[i]);                                 //显示出来
					  printf("internal_buf[%d]内存申请之后的地址为:%#x\r\n",i,(u32)(internal_buf[i]));
					  printf("internal_buf[%d]内存申请成功!\r\n",i);
				 }
				 else if(err == OS_ERR_MEM_NO_FREE_BLKS) //内存块不足
				 {
					  LCD_ShowString(30,180,200,16,16,"INTERNAL_MEM Empty!  ");
				 }
			      delay_ms(500);            //延时500ms,也就是每500ms申请一次内存,共五次
			}
				break;
			  case KEY1_PRES:
				for(i=5;i>0;i--)
			  {
					if(internal_buf[i-1] != NULL)              //internal_buf不为空就释放内存
					{
						OSMemPut(INTERNAL_MEM,internal_buf[i-1]);//循环5次释放内部内存
						printf("internal_buf[%d]内存释放之后的地址为:%#x\r\n",i-1,(u32)(internal_buf[i-1]));
						printf("internal_buf[%d]内存释放成功!\r\n",i-1);
						internal_buf[i-1]=NULL;                  //释放后的缓存区指向NULL!
					}
					else if(internal_buf[i-1] == NULL)        //内存已释放
				 {
					  LCD_ShowString(30,180,200,16,16,"INTERNAL_MEM released!  ");
				 }
			   delay_ms(500);            //延时500ms,也就是每500ms申请一次内存,共五次
				}
				break;
			case KEY2_PRES:           //按下KEY2键:申请外部内存
				external_buf=OSMemGet(EXTERNAL_MEM,&err);
				if(err == OS_ERR_NONE) //内存申请成功
				{
					printf("external_buf内存申请之后的地址为:%#x\r\n",(u32)(external_buf));
					LCD_ShowString(30,260,200,16,16,"Memory Get success!  ");
					external_memget_num++;
					POINT_COLOR = BLUE;
					sprintf((char*)external_buf,"EXTERNAL_MEM Use %d times",external_memget_num);
					LCD_ShowString(30,276,200,16,16,external_buf); 
					POINT_COLOR = RED;
				}
				if(err == OS_ERR_MEM_NO_FREE_BLKS) //内存块不足
				{
					LCD_ShowString(30,260,200,16,16,"EXTERNAL_MEM Empty!  ");
				}
				break;
			case KEY0_PRES:
				if(external_buf != NULL) //external_buf不为空就释放内存
				{
					OSMemPut(EXTERNAL_MEM,external_buf);//释放外部内存
					printf("external_buf内存释放之后的地址为:%#x\r\n",(u32)(external_buf));
					LCD_ShowString(30,260,200,16,16,"Memory Put success!   ");
				}
				break;
		}
		num++;
		if(num==50)
		{
			num=0;
			LED0 = ~LED0;
		}
		delay_ms(10);          //延时10ms
	}
}

//内存管理任务(显示总的内存缓存块数以及剩余缓存块的数)
void memmanage_task(void *p_arg)
{
	LCD_ShowString(5,164,200,16,16,"Total:  Remain:");
	LCD_ShowString(5,244,200,16,16,"Total:  Remain:");
	while(1)
	{
		POINT_COLOR = BLUE;
		LCD_ShowxNum(53,164,INTERNAL_MEM->OSMemNBlks,1,16,0);	
		LCD_ShowxNum(125,164,INTERNAL_MEM->OSMemNFree,1,16,0);	
		LCD_ShowxNum(53,244,EXTERNAL_MEM->OSMemNBlks,1,16,0);	
		LCD_ShowxNum(125,244,EXTERNAL_MEM->OSMemNFree,1,16,0);	
		POINT_COLOR = RED;
		delay_ms(100);       //延时100ms
	}
}

五.总结

1.通过定义一个二维数组在内存中划分一个内存分区,其中的所有内存块应大小相等。
2.系统通过与内存分区相关联的内存控制块来对内存分区进行管理。
3.划分及创建内存分区根据需要由应用程序负责,而系统只提供了可供任务调用的相关函数。
4.在UCOSII中,在使用和释放动态内存的安全性方便,要由应用程序全权负责。

六. 感谢支持

    完结撒花!希望看到这里的小伙伴能点个关注,我后续会持续更新,也欢迎大家广泛交流。
    码字实属不易,如果本文对你有10分帮助,就赏个10分把,感谢各位大佬支持!

在这里插入图片描述

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tutu-hu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值