序言
在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
}
}
具体的实验现象,自己烧录到开发板看一下就知道了