1、什么是任务的挂起
任务在创建后将从睡眠态转换到就绪态,就绪的任务可以通过调用函数(OSTaskSuspend),剥夺起CPU的使用权,而使其暂时中止运行,转到阻塞状态。这个过程叫做挂起任务。
image:排队排的好好的(这个情景不对- -),突然天降大手将你抓起,高高挂起【有一点不恰当,任务是可以自己挂起自己的】。
2、为什么要任务挂起
一个任务如果无事可做,且优先级又高,长期占有CPU,使其他任务得不到运行而“饿死”。这时我们便需要采取“挂起”的策略;当然解决这个问题的方法不止一种,还有任务延时等策略,将会在后面学习到,这里先挖个坑。
3、什么是任务的恢复
被挂起的任务不能运行,直到其他任务以该任务的优先级作为参数调用函数(OSTaskResume)恢复,才能使之重新设置为就绪态。
image:当你知错的时候(必要时),大手根据你被挂起的位置(优先级),将你重新放回等待队列【没有自己恢复自己一说】。
4、tip:阻塞态和挂起态是两种不同的状态(继续挖坑)。
5、任务阻塞函数:OSTaskSuspend
流程图
源码:
1 #if OS_TASK_SUSPEND_EN > 0 2 INT8U OSTaskSuspend (INT8U prio) 3 { 4 BOOLEAN self; 5 OS_TCB *ptcb; 6 INT8U y; 7 #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ 8 OS_CPU_SR cpu_sr = 0; 9 #endif 10 11 12 13 #if OS_ARG_CHK_EN > 0 14 if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to suspend idle task */ 15 return (OS_ERR_TASK_SUSPEND_IDLE); //不允许阻止空闲任务 16 } 17 if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */ 18 if (prio != OS_PRIO_SELF) { 19 return (OS_ERR_PRIO_INVALID); //非法的优先级 20 } 21 } 22 #endif
这段代码对参数的检查,其间有两个宏定义,之后便关中断,进入全局定义。
1 OS_ENTER_CRITICAL(); 2 if (prio == OS_PRIO_SELF) { /* See if suspend SELF */ 3 prio = OSTCBCur->OSTCBPrio; 4 self = OS_TRUE; 5 } else if (prio == OSTCBCur->OSTCBPrio) { /* See if suspending self */ 6 self = OS_TRUE; 7 } else { 8 self = OS_FALSE; /* No suspending another task */ 9 } 10 ptcb = OSTCBPrioTbl[prio]; 11 if (ptcb == (OS_TCB *)0) { /* Task to suspend must exist */ 12 OS_EXIT_CRITICAL(); 13 return (OS_ERR_TASK_SUSPEND_PRIO); //要挂起的任务不存在 14 } 15 if (ptcb == OS_TCB_RESERVED) { /* See if assigned to Mutex */ 16 OS_EXIT_CRITICAL(); 17 return (OS_ERR_TASK_NOT_EXIST); //要挂起使用互斥信号量的任务 18 }
line 1~ line 9:如果优先级是OS_PRIO_SELF,取得自己真正的优先级,注意这里对变量self的赋值;
line 11~ line 18:好像依然是参数检查,因为这里的参数检查涉及到全局变量,所以得在关中断后。
1 y = ptcb->OSTCBY; 2 OSRdyTbl[y] &= ~ptcb->OSTCBBitX; /* Make task not ready */ 3 if (OSRdyTbl[y] == 0) { 4 OSRdyGrp &= ~ptcb->OSTCBBitY; 5 } 6 ptcb->OSTCBStat |= OS_STAT_SUSPEND; /* Status of task is 'SUSPENDED' */ 7 OS_EXIT_CRITICAL(); 8 if (self == OS_TRUE) { /* Context switch only if SELF */ 9 OS_Sched(); /* Find new highest priority task */ 10 } 11 return (OS_ERR_NONE); //成功挂起一个任务 12 } 13 #endif
line 1~ line 5,很有意思,是取消就绪表和就绪组中的就绪标志。
line 6的注释,说明任务是在这句被挂起了(不涉及具体数值的分析)。【注红是为了表示这个才是整个函数的核心】
line 7~ line 8,如果任务是挂起自己(self在这里用到),这时才失去CPU的使用权。
6、任务恢复函数:OSTaskResume
流程图
源码:
1 #if OS_TASK_SUSPEND_EN > 0 2 INT8U OSTaskResume (INT8U prio) 3 { 4 OS_TCB *ptcb; 5 #if OS_CRITICAL_METHOD == 3 /* Storage for CPU status register */ 6 OS_CPU_SR cpu_sr = 0; 7 #endif 8 9 #if OS_ARG_CHK_EN > 0 10 if (prio >= OS_LOWEST_PRIO) { /* Make sure task priority is valid */ 11 return (OS_ERR_PRIO_INVALID); //非法优先级 12 } 13 #endif
惯例,参数检查。
1 OS_ENTER_CRITICAL(); 2 ptcb = OSTCBPrioTbl[prio]; 3 if (ptcb == (OS_TCB *)0) { /* Task to suspend must exist */ 4 OS_EXIT_CRITICAL(); 5 return (OS_ERR_TASK_RESUME_PRIO); //要恢复的任务不存在 6 } 7 if (ptcb == OS_TCB_RESERVED) { /* See if assigned to Mutex */ 8 OS_EXIT_CRITICAL(); 9 return (OS_ERR_TASK_NOT_EXIST); //要挂起使用互斥信号量的任务
10 }
涉及全局变量的参数检查。
1 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) != OS_STAT_RDY) { /* Task must be suspended */ 2 ptcb->OSTCBStat &= ~(INT8U)OS_STAT_SUSPEND; /* Remove suspension */ 3 if (ptcb->OSTCBStat == OS_STAT_RDY) { /* See if task is now ready */ 4 if (ptcb->OSTCBDly == 0) { 5 OSRdyGrp |= ptcb->OSTCBBitY; /* Yes, Make task ready to run */ 6 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; 7 OS_EXIT_CRITICAL(); 8 if (OSRunning == OS_TRUE) { 9 OS_Sched(); /* Find new highest priority task */ 10 } 11 } else { 12 OS_EXIT_CRITICAL(); 13 } 14 } else { /* Must be pending on event */ 15 OS_EXIT_CRITICAL(); 16 } 17 return (OS_ERR_NONE); 18 } 19 OS_EXIT_CRITICAL(); 20 return (OS_ERR_TASK_NOT_SUSPENDED); 21 } 22 #endif
阻塞和恢复是一对逆过程,在阻塞中标志OSTCBStat以示任务被挂起,当然在恢复函数中要对OSTCBStat操作,使其就绪。
line 1判断该任务是否为挂起状态;
line 2移除了挂起的标志;
line 3判断OSTCBStat是否为0,因为任务阻塞的原因不一样。假如任务因为等待信号量而被阻塞,那么即使清除了挂起标志,OSTCBStat还不为0。即任务还在等待诸如信号量、邮箱、队列等事件的发生,因此仍不能就绪。
line 4判断任务是否还在等待延时,如果是,也不能进入就绪态。
7、链接
很巧,找资料时又看到那位大神自己玩任务的挂起和恢复,继续mark:ucos任务的挂起和恢复。
8、任务的调度
自上次任务的删除开始,我们便接触到了任务调度这个概念。这次两个函数里都有涉及到一个叫做OS_Sched()这个函数。
下一节便是学习任务调度。