UCOSIII的任务管理

序言

任务管理,也叫做进程管理,是任何一个操作系统都必须面对的核心问题之一,UCOSIII同样不例外,在UCOSIII中与任务管理相关的API在os_task.c这个文件中实现。下面我们将会从四个大方向介绍UCOSIII的任务管理:相关文件概览、部分API实际应用,API详解,相关数据结构介绍。

相关文件概览

os_task.c文件概览

看了一下这个文件有2550行,包含如下的函数

  • 改变任务优先级:OSTaskChangePrio
  • 创建一个任务:OSTaskCreate
  • 删除一个任务:OSTaskDel
  • 消息队列清除:OSTaskQFlush
  • 等待消息:OSTaskQPend
  • 取消等待:OSTaskQPendAbort
  • 向任务发送消息:OSTaskQPost
  • 获取一个任务寄存器的当前值:OSTaskRegGet
  • 获取下一个可获得的任务寄存器的ID:OSTaskRegGetID
  • 设置一个任务寄存器的当前值:OSTaskRegSet
  • 恢复一个悬挂的任务:OSTaskResume
  • 等待一个任务信号:OSTaskSemPend
  • 取消等待一个任务信号:OSTaskSemPendAbort
  • 向一个任务发送信号:OSTaskSemPost
  • 设置任务的信号计数器:OSTaskSemSet
  • 堆栈检查:OSTaskStkChk
  • 挂起一个任务:OSTaskSuspend
  • 改变一个任务的时间片:OSTaskTimeQuantaSet
  • 添加任务到调试列表:OS_TaskDbgListAdd
  • 任务管理初始化:OS_TaskInit
  • 初始化任务控制块:OS_TaskInitTCB
  • 发送消息到一个任务:OS_TaskQPost
  • 恢复一个挂起的任务:OS_TaskResume
  • 抓住偶然的任务返回:OS_TaskReturn
  • 向一个任务发送一个信号:OS_TaskSemPost
  • 挂起一个任务:OS_TaskSuspend

os_core.c文件概览

这个文件有2642行,包含如下函数

  • 内核初始化:OSInit
  • 通知UCOSIII任务将要进入中断:OSIntEnter
  • 通知UCOSIII任务将要退出中断:OSIntExit
  • 任务调度:OSSched
  • 任务调度器上锁:OSSchedLock
  • 任务调度器解锁:OSSchedUnlock
  • 改变时间片轮转调度属性:OSSchedRoundRobinCfg
  • 提前放弃时间片:OSSchedRoundRobinYield
  • 开始多任务处理:OSStart
  • 获取UCOSIII版本号:OSVersion
  • 空闲任务:OS_IdleTask
  • 空闲任务初始化:OS_IdleTaskInit
  • 将一个任务锁在某个事件上:OS_Pend
  • 退出等待某个事件:OS_PendAbort
  • 初始化一个等待列表:OS_PendListInit
  • 向一个任务发送消息:OS_Post
  • 就绪列表初始化:OS_RdyListInit

部分API实际应用

任务创建和删除

在使用UCOSIII的时候,我们需要按照一定的顺序来初始化并打开UCOSIII,可以按照如下步骤来:

  1. 调用OS_init()初始化UCOSIII
  2. 使用OS_CRITICAL_ENTER()进入临界区
  3. 使用OSTaskCreate创建任务,一般在main()函数里面创建一个AppTaskStart任务,其它的任务由它创建
  4. 使用OS_CRITICAL_ENTER()退出临界区
  5. 调用OSStart开启UCOS

如下所示(看不懂的部分先忽略,以后的东西以后再学),仔细看注释,另外两个任务的函数定义我没写出来,写出来太长了。

//首先要定义任务控制块和任务堆栈以及任务函数
/*
/************************************************************
*									LOCAL GLOBAL VARIABLES
*************************************************************
*/
static OS_TCB AppTaskStartTCB;
static CPU_STK AppTaskStartTCBStk[APP_CFG_TASK_START_STK_SIZE];

#define TASK1_TASK_PRIO		4
#define TASK1_STK_SIZE 		128
static OS_TCB Task1_TaskTCB;
static CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];


#define TASK2_TASK_PRIO		5	
#define TASK2_STK_SIZE 		128
static OS_TCB Task2_TaskTCB;
static CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];

/*
/************************************************************
*									FUNCTION PROTOTYPES
*************************************************************
*/
static void AppTaskStart(void *p_arg);
static void task1_task(void *p_arg);
static void task2_task(void *p_arg);


int main()
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	uart_init(115200);
	LED_Init();
	
	OSInit(&err);//1. 调用OS_init()初始化UCOSIII
	if(err != OS_ERR_NONE){
		/* Do something */
	}
	OS_CRITICAL_ENTER();//2. 使用OS_CRITICAL_ENTER()进入临界区
  	OSTaskCreate ((OS_TCB      * )&AppTaskStartTCB,//3. 使用OSTaskCreate创建任务
                (CPU_CHAR    * )"start task",
                (OS_TASK_PTR   )AppTaskStart,
                (void        * )0,
                (OS_PRIO       )APP_CFG_TASK_START_PRIO,
                (CPU_STK     * )&AppTaskStartTCBStk[0],
                (CPU_STK_SIZE  )APP_CFG_TASK_START_STK_SIZE/10,
                (CPU_STK_SIZE  )APP_CFG_TASK_START_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);
	
	if(err != OS_ERR_NONE){
		/* Do something */
	}
	
	OS_CRITICAL_EXIT();//4. 使用OS_CRITICAL_ENTER()退出临界区
	OSStart(&err);//5. 调用OSStart开启UCOS
	
	if(err != OS_ERR_NONE){
		/* Do something */
	}
}

//编写任务函数
//AppTaskStart任务函数
static void AppTaskStart(void *p_arg)
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	CPU_Init();
	
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	//统计任务                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了测量中断关闭时间
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //当使用时间片轮转的时候
	 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif	
	
	OS_CRITICAL_ENTER();	//进入临界区
	//创建TASK1任务
	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,		
							 (CPU_CHAR	* )"Task1 task", 		
               (OS_TASK_PTR )task1_task, 			
               (void		* )0,					
               (OS_PRIO	  )TASK1_TASK_PRIO,     
               (CPU_STK   * )&TASK1_TASK_STK[0],	
               (CPU_STK_SIZE)TASK1_STK_SIZE/10,	
               (CPU_STK_SIZE)TASK1_STK_SIZE,		
               (OS_MSG_QTY  )0,					
               (OS_TICK	  )2,					
               (void   	* )0,					
               (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
               (OS_ERR 	* )&err);				
				 
	//创建TASK2任务
	OSTaskCreate((OS_TCB 	* )&Task2_TaskTCB,		
							 (CPU_CHAR	* )"task2 task", 		
               (OS_TASK_PTR )task2_task, 			
               (void		* )0,					
               (OS_PRIO	  )TASK2_TASK_PRIO,     	
               (CPU_STK   * )&TASK2_TASK_STK[0],	
               (CPU_STK_SIZE)TASK2_STK_SIZE/10,	
               (CPU_STK_SIZE)TASK2_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);						 
	OS_CRITICAL_EXIT();	//退出临界区
	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
}

我们用到了两个在os_task.c文件中定义的函数,一个是OSTaskCreate,另一个是OSTaskDel函数,前者用来创建任务,后者用来删除任务,这里之所以用到了删除,是因为开始任务(AppTaskStart)的终极目标就是创建其它的任务,一旦其它任务创建完成,它的使命也就结束了,所以自然要被删除掉。

任务挂起和恢复

这里我们就用到了上面没写出来的两个任务函数

void task1_task(void *p_arg)
{
	u8 task1_num=0;
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(5,110,115,314); 	//画一个矩形	
	LCD_DrawLine(5,130,115,130);		//画线
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"Task1 Run:000");
	OS_CRITICAL_EXIT();
	while(1)
	{
		task1_num++;	//任务1执行次数加1 注意task1_num1加到255的时候会清零!!
		LED0= ~LED0;
		printf("任务1已经执行:%d次\r\n",task1_num);
		if(task1_num==5) 
		{
			OSTaskSuspend((OS_TCB*)&Task2_TaskTCB,&err);//任务1执行5次后挂起任务2
			printf("任务1挂起了任务2!\r\n");
		}
		if(task1_num==10) 
		{
			OSTaskResume((OS_TCB*)&Task2_TaskTCB,&err);	//任务1运行10次后恢复任务2
			printf("任务1恢复了任务2!\r\n");
		}
		LCD_Fill(6,131,114,313,lcd_discolor[task1_num%14]); //填充区域
		LCD_ShowxNum(86,111,task1_num,3,16,0x80);	//显示任务执行次数
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
		
	}
}

在这个任务中,我们有一个局部变量task1_num,当task1_num为5的时候,我们会利用OSTaskSuspend函数将任务2挂起(任务2没有写出来),当task1_num为10的时候,我们会利用OSTaskResume恢复任务2。

API详解

我们在上面遇到了四个重要的任务相关的API

OSTaskCreate

这个函数是用来让UCOSIII能够管理任务的执行的,任务要么在多任务管理开始前被创建,要么被正在运行的任务创建,不可以通过外部中断来创建任务。

void  OSTaskCreate (OS_TCB        *p_tcb,
                    CPU_CHAR      *p_name,
                    OS_TASK_PTR    p_task,
                    void          *p_arg,
                    OS_PRIO        prio,
                    CPU_STK       *p_stk_base,
                    CPU_STK_SIZE   stk_limit,
                    CPU_STK_SIZE   stk_size,
                    OS_MSG_QTY     q_size,
                    OS_TICK        time_quanta,
                    void          *p_ext,
                    OS_OPT         opt,
                    OS_ERR        *p_err)

上面是它的函数原型,下面我们来一个一个介绍这些参数

  • *p_tcb:一个TCB指针
  • *p_name:一个字符串指针,用来给任务提供一个名字
  • p_task:一个任务函数指针
  • *p_arg:传递给任务函数的参数
  • prio:任务优先级
  • *p_stk_base:堆栈基址
  • *stk_limit:任务堆栈深度限位
  • *stk_size:任务堆栈大小
  • q_size:可以被发送给这个任务的消息的最大数量
  • time_quanta:时间片轮转调度的时间片数量
  • *p_ext:用户补充的存储区
  • opt:包含这个任务一些额外的信息
  • *p_err:存放该函数错误时的返回值

OSTaskDel

这个函数允许你删除一个任务。主调任务可以通过给p_tcb赋予一个NULL指针来删除自身。被删除的任务回到休眠态,可以被创建函数再次被激活。

void  OSTaskDel (OS_TCB  *p_tcb,
                 OS_ERR  *p_err)
  • *p_tcb:要被删除的任务的控制块
  • *p_err:一个指向错误码的指针

OSTaskSuspend

这个函数用来挂起一个任务,如果p_tcb参数是NULL,函数将挂起主调任务自身,如果不是,则挂起p_tcb对应的任务

void   OSTaskSuspend (OS_TCB  *p_tcb,
                      OS_ERR  *p_err)
  • *p_tcb:将要被挂起的任务的TCB的指针
  • *p_err:一个指向错误码的指针

OSTaskResume

这个函数被用来恢复一个曾经被挂起的任务,这是唯一的可以移除精确函数挂起的调用

void  OSTaskResume (OS_TCB  *p_tcb,
                    OS_ERR  *p_err)
  • *p_tcb:将要被恢复的任务的TCB的指针
  • *p_err:一个指向错误码的指针

任务状态

我们上面学习了四个任务调度函数:创建任务,删除任务,挂起任务,恢复任务。这些任务在这些状态之间切换,那么到底是哪些状态呢,我们在这里单独介绍一下任务状态。

任务状态描述
休眠态任务只是以任务函数的形式存在,只是存储区的一段代码
就绪态任务在就续表中已经登记,等待获取CPU
运行态正在运行的任务处于运行态
等待态正在运行的任务需要等待某一个事件,比如信号量、消息、事件标志组等,暂时会让出CPU
中断服务态一个正在运行的任务被中断打断,CPU转而执行中断服务程序,这时这个任务就会被挂起,进入中断服务状态

任务的状态可以在这四种状态之间切换,至于具体的切换机制,我们在后面的学习中会慢慢了解到

相关数据结构介绍

在上面的API使用中,我们用到了几种数据结构,它们分别是任务控制块TCB,任务堆栈CPU_STK,还有我们没有直接用到的任务就绪表,下面我们一一介绍这些数据结构

任务控制块OS_TCB

这是一个非常长的数据结构,如下所示

/*
------------------------------------------------------------------------------------------------------------------------
*                                                  TASK CONTROL BLOCK
------------------------------------------------------------------------------------------------------------------------
*/

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
};

具体的每一个意思在注释中都有,如果看不懂英文,可以一边看中文一边看英文,注意一定要把每一个成员的含义看一下,能看懂多少看多少。

任务堆栈CPU_STK

任务堆栈用来在切换任务和调用其它函数的时候保存现场的,因此每个任务都要有自己的堆栈。
从cpu.h中我们可以知道,CPU_STK其实就是CPU_INT32U,所以一个堆栈其实就是一个无符号整形数组

CPU_STK TASK_STK[64]

这句话的含义就是定义一个包含有64个无符号整数的数组

任务就绪表

UCOSIII中就绪的任务是放在任务就续表里的,任务就续表分为两个部分:优先级位映射表OSPrioTbl[]和就绪任务列表OSRdyList[]。

位优先级映射表

我们有一个数组OSPrioTbl[],它的第一个元素为OSPrioTbl[0],这个元素的最高位为最高优先级,最低位为最低优先级,它的第二个元素为OSPrioTbl[1],这个元素的最高位所代表的优先级正好在上一个元素的下面,依此类推下去。

综上所述:用一个数组OSPrioTbl[]来表示任务优先级状态,其中的一个位代表一个优先级。

就绪任务列表

OSRdyList[]是用来记录每一个优先级下所有就绪的任务,它在os.h中定义,数组元素的类型位OS_RDY_LIST,这是一个结构体,如下所示

struct os_rdy_list{
    OS_TCB *HeadPtr;
    OS_TCB *TailPtr;
    OS_OBJ_QTY NbrEntries;
}

UCOSIII支持时间片轮转调度,所以一个优先级下面可以有多个任务,我们需要对这些任务进行管理,OSRdyList[]中的每一个元素管理一个优先级的所有任务,这个元素是一个结构体,用来维护一个双向链表,这个链表里面是这个优先级下面的所有任务的控制块。

与任务就绪列表相关的函数在os_core.c中定义,如下是一些常用函数

函数描述
OS_RdyListInit()由OSInit()调用用来初始化并清空任务就绪列表
OS_RdyListInsertHead()向某一优先级下的任务双向链表头部添加一个任务控制块TCB
OS_RdyListInsertTail()向某一优先级下的任务双向链表尾部添加一个任务控制块TCB
OS_RdyListRemove()将任务控制块TCB从任务就绪列表中删除
OS_RdyListInsertTail()将一个任务控制块TCB从双向链表的头部移到尾部
OS_RdyListInsert()在就续表中添加一个任务控制块TCB

总结

重点掌握启用UCOSIII的基本步骤

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值