UCOSIII中的内存管理

序言

在UCOSIII中有内存管理模块,使用内存管理模块可以动态地分配和释放内存,这样可以高效的使用内存资源,本篇文章,我们将会介绍一下UCOSIII中的内存管理功能。

首先我会介绍UCOSIII中的内存管理是什么,然后介绍相关的函数,最后是一个小实验用来验证我们对知识的理解。

UCOSIII中的存储管理是什么

学习C语言的我们都会使用malloc和free这两个函数来申请和释放内存,我们在使用Keil编写STM32的时候也可以使用malloc和free,但是不建议这么使用,这样做会将原来大块内存逐渐分割成很多小内存块,产生大量的内存碎片。

UCOSIII提供了自己的动态内存方案,UCOSIII将存储空间分成区和块,一个存储区有数个固定大小的库组成,如下图所示

在这里插入图片描述

一般存储区是固定的,在程序中可以用数组来表示一个存储区,比如u8 buffer[20][10]就表示l一个有20个存储块,每个存储块有10个字节的存储区。如果我们定义的存储区在程序运行期间都不会被删除掉,一直有效,那么存储区内存也可以使用Malloc来分配。在创建存储区以后,应用程序就可以获得固定大小的存储块了。

相关数据结构与函数

OS_MEM

在使用内存管理之前首先要创建存储区,在创建存储区之前我们先了解一个重要的结构体,存储区控制块:OS_MEM,结构体OS_MEM如下:

struct os_tcb {
    CPU_STK             *StkPtr;                            /* Pointer to current top of stack                        */

    void                *ExtPtr;                            /* Pointer to user definable data for TCB extension       */

    CPU_STK             *StkLimitPtr;                       /* Pointer used to set stack 'watermark' limit            */

    OS_TCB              *NextPtr;                           /* Pointer to next     TCB in the TCB list                */
    OS_TCB              *PrevPtr;                           /* Pointer to previous TCB in the TCB list                */

    OS_TCB              *TickNextPtr;
    OS_TCB              *TickPrevPtr;

    OS_TICK_SPOKE       *TickSpokePtr;                      /* Pointer to tick spoke if task is in the tick list      */

    CPU_CHAR            *NamePtr;                           /* Pointer to task name                                   */

    CPU_STK             *StkBasePtr;                        /* Pointer to base address of stack                       */

#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
    OS_TLS               TLS_Tbl[OS_CFG_TLS_TBL_SIZE];
#endif

    OS_TASK_PTR          TaskEntryAddr;                     /* Pointer to task entry point address                    */
    void                *TaskEntryArg;                      /* Argument passed to task when it was created            */

    OS_PEND_DATA        *PendDataTblPtr;                    /* Pointer to list containing objects pended on           */
    OS_STATE             PendOn;                            /* Indicates what task is pending on                      */
    OS_STATUS            PendStatus;                        /* Pend status                                            */

    OS_STATE             TaskState;                         /* See OS_TASK_STATE_xxx                                  */
    OS_PRIO              Prio;                              /* Task priority (0 == highest)                           */
    CPU_STK_SIZE         StkSize;                           /* Size of task stack (in number of stack elements)       */
    OS_OPT               Opt;                               /* Task options as passed by OSTaskCreate()               */

    OS_OBJ_QTY           PendDataTblEntries;                /* Size of array of objects to pend on                    */

    CPU_TS               TS;                                /* Timestamp                                              */

    OS_SEM_CTR           SemCtr;                            /* Task specific semaphore counter                        */

                                                            /* DELAY / TIMEOUT                                        */
    OS_TICK              TickCtrPrev;                       /* Previous time when task was            ready           */
    OS_TICK              TickCtrMatch;                      /* Absolute time when task is going to be ready           */
    OS_TICK              TickRemain;                        /* Number of ticks remaining for a match (updated at ...  */
                                                            /* ... run-time by OS_StatTask()                          */
    OS_TICK              TimeQuanta;
    OS_TICK              TimeQuantaCtr;

#if OS_MSG_EN > 0u
    void                *MsgPtr;                            /* Message received                                       */
    OS_MSG_SIZE          MsgSize;
#endif

#if OS_CFG_TASK_Q_EN > 0u
    OS_MSG_Q             MsgQ;                              /* Message queue associated with task                     */
#if OS_CFG_TASK_PROFILE_EN > 0u
    CPU_TS               MsgQPendTime;                      /* Time it took for signal to be received                 */
    CPU_TS               MsgQPendTimeMax;                   /* Max amount of time it took for signal to be received   */
#endif
#endif

#if OS_CFG_TASK_REG_TBL_SIZE > 0u
    OS_REG               RegTbl[OS_CFG_TASK_REG_TBL_SIZE];  /* Task specific registers                                */
#endif

#if OS_CFG_FLAG_EN > 0u
    OS_FLAGS             FlagsPend;                         /* Event flag(s) to wait on                               */
    OS_FLAGS             FlagsRdy;                          /* Event flags that made task ready to run                */
    OS_OPT               FlagsOpt;                          /* Options (See OS_OPT_FLAG_xxx)                          */
#endif

#if OS_CFG_TASK_SUSPEND_EN > 0u
    OS_NESTING_CTR       SuspendCtr;                        /* Nesting counter for OSTaskSuspend()                    */
#endif

#if OS_CFG_TASK_PROFILE_EN > 0u
    OS_CPU_USAGE         CPUUsage;                          /* CPU Usage of task (0.00-100.00%)                       */
    OS_CPU_USAGE         CPUUsageMax;                       /* CPU Usage of task (0.00-100.00%) - Peak                */
    OS_CTX_SW_CTR        CtxSwCtr;                          /* Number of time the task was switched in                */
    CPU_TS               CyclesDelta;                       /* value of OS_TS_GET() - .CyclesStart                    */
    CPU_TS               CyclesStart;                       /* Snapshot of cycle counter at start of task resumption  */
    OS_CYCLES            CyclesTotal;                       /* Total number of # of cycles the task has been running  */
    OS_CYCLES            CyclesTotalPrev;                   /* Snapshot of previous # of cycles                       */

    CPU_TS               SemPendTime;                       /* Time it took for signal to be received                 */
    CPU_TS               SemPendTimeMax;                    /* Max amount of time it took for signal to be received   */
#endif

#if OS_CFG_STAT_TASK_STK_CHK_EN > 0u
    CPU_STK_SIZE         StkUsed;                           /* Number of stack elements used from the stack           */
    CPU_STK_SIZE         StkFree;                           /* Number of stack elements free on   the stack           */
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
    CPU_TS               IntDisTimeMax;                     /* Maximum interrupt disable time                         */
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
    CPU_TS               SchedLockTimeMax;                  /* Maximum scheduler lock time                            */
#endif

#if OS_CFG_DBG_EN > 0u
    OS_TCB              *DbgPrevPtr;
    OS_TCB              *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
};

我们这里只介绍一些重要的相关的成员,其余的现在不看,就像王爽老师说的:以后的东西以后在学

  • AddrPtr:指向存储区起始地址
  • NamePtr:指向存储区名字
  • FreeListPtr:指向空闲存储块
  • BlkSize:存储区中存储块大小,单位:字节
  • NbrMax:存储区中总的存储块数
  • NbrFree:存储区中空闲存储块数

OSMemCreate

这个函数是用来创建存储区的,下面是函数原型

void  OSMemCreate (OS_MEM       *p_mem,
                   CPU_CHAR     *p_name,
                   void         *p_addr,
                   OS_MEM_QTY    n_blks,
                   OS_MEM_SIZE   blk_size,
                   OS_ERR       *p_err)
  • p_mem:指向存储区控制块地址,一般有用户程序定义一个OS_MEM结构体。
  • p_name:指向存储区的名字,我们可以给存储区取一个名字
  • p_addr:存储区所有存储空间基地址
  • n_blks:存储区中存储块个数
  • blk_size:存储块大小
  • p_err:返回的错误码

OSMemGet

我们使用函数OSMemGet来获取存储块,函数原型如下

void  *OSMemGet (OS_MEM  *p_mem,
                 OS_ERR  *p_err)
  • p_mem:要使用的存储区
  • p_err:返回的错误码

OSMemPut

我们使用这个函数来完成存储块的释放操作,函数原型如下

void  OSMemPut (OS_MEM  *p_mem,
                void    *p_blk,
                OS_ERR  *p_err)

存储管理实验

实验概述

我们设计一个程序,创建一个存储区,这个存储区创建STM32的内部RAM,通过开发板上的按键来申请和释放内存

具体实验

定义一个存储区控制块以及一片数组存储区

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

创建存储区控制块和两个任务

	//创建一个存储分区
	OSMemCreate((OS_MEM*	)&INTERNAL_MEM,
				(CPU_CHAR*	)"Internal Mem",
				(void*		)&Internal_RamMemp[0][0],
				(OS_MEM_QTY	)INTERNAL_MEM_NUM,
				(OS_MEM_SIZE)INTERNAL_MEMBLOCK_SIZE,
				(OS_ERR*	)&err);

	//创建主任务
	OSTaskCreate((OS_TCB*     )&Main_TaskTCB,		
				 (CPU_CHAR*   )"Main task", 		
                 (OS_TASK_PTR )main_task, 			
                 (void*       )0,					
                 (OS_PRIO	  )MAIN_TASK_PRIO,     
                 (CPU_STK*    )&MAIN_TASK_STK[0],	
                 (CPU_STK_SIZE)MAIN_STK_SIZE/10,	
                 (CPU_STK_SIZE)MAIN_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void*       )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR*     )&err);
	//创建一个内存块检查任务
	OSTaskCreate((OS_TCB*     )&MemManage_TaskTCB,		
				 (CPU_CHAR*   )"MemManage task", 		
                 (OS_TASK_PTR )memmanage_task, 			
                 (void*       )0,					
                 (OS_PRIO	  )MEMMANAGE_TASK_PRIO,     
                 (CPU_STK*    )&MEMMANAGE_TASK_STK[0],	
                 (CPU_STK_SIZE)MEMMANAGE_STK_SIZE/10,	
                 (CPU_STK_SIZE)MEMMANAGE_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void*       )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR*     )&err);

下面是两个任务的实现

//主任务的任务函数
void main_task(void *p_arg)
{
	u8 key,num;
	static u8 internal_memget_num;
	CPU_INT08U *internal_buf;
	OS_ERR err;
	while(1)
	{
		key = KEY_Scan(0);  //扫描按键
		switch(key)
		{
			case WKUP_PRES:		//按下KEY_UP键
				internal_buf=OSMemGet((OS_MEM*)&INTERNAL_MEM,
								      (OS_ERR*)&err);
				if(err == OS_ERR_NONE) //内存申请成功
				{
					printf("internal_buf内存申请之后的地址为:%#x\r\n",(u32)(internal_buf));
					LCD_ShowString(30,155,200,16,16,"Memory Get success!  ");
					internal_memget_num++;
					POINT_COLOR = BLUE;
					sprintf((char*)internal_buf,"INTERNAL_MEM Use %d times",internal_memget_num);
					LCD_ShowString(30,175,200,16,16,internal_buf); 
					POINT_COLOR = RED;
				}
				if(err == OS_ERR_MEM_NO_FREE_BLKS) //内存块不足
				{
					LCD_ShowString(30,155,200,16,16,"INTERNAL_MEM Empty!  ");
				}
				break;
			case KEY1_PRES:
				if(internal_buf != NULL) //internal_buf不为空就释放内存
				{
					OSMemPut((OS_MEM*	)&INTERNAL_MEM,		//释放内存
							 (void*		)internal_buf,
							 (OS_ERR* 	)&err);
					printf("internal_buf内存释放之后的地址为:%#x\r\n",(u32)(internal_buf));
					LCD_ShowString(30,155,200,16,16,"Memory Put success!   ");
				}
				break;
		}
	
		num++;
		if(num==50)
		{
			num=0;
			LED0 = ~LED0;
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
	}
}

//内存管理任务
void memmanage_task(void *p_arg)
{
	OS_ERR err;
	LCD_ShowString(5,135,200,16,16,"Total:  Remain:");
	while(1)
	{
		POINT_COLOR = BLUE;
		LCD_ShowxNum(53,135,INTERNAL_MEM.NbrMax,1,16,0);	
		LCD_ShowxNum(125,135,INTERNAL_MEM.NbrFree,1,16,0);		
		POINT_COLOR = RED;
		OSTimeDlyHMSM(0,0,0,100,OS_OPT_TIME_PERIODIC,&err);//延时100ms
	}
}

具体的实验现象,自己烧录到开发板看一下就知道了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值