您的位置:首页 > 其它

删除任务,OSTaskDel()

2017-03-09 10:09 288 查看
有时候删除任务是很有必要的。删除任务,是说任务将返回并处于休眠状态(参看3.02,任务状态),并不是说任务的代码被删除了,只是任务的代码不再被µC/OS-Ⅱ调用。通过调用OSTaskDel()就可以完成删除任务的功能(如程序清单 L4.11所示)。OSTaskDel()一开始应确保用户所要删除的任务并非是空闲任务,因为删除空闲任务是不允许的[L4.11(1)]。不过,用户可以删除statistic任务[L4.11(2)]。接着,OSTaskDel()还应确保用户不是在ISR例程中去试图删除一个任务,因为这也是不被允许的[L4.11(3)]。调用此函数的任务可以通过指定OS_PRIO_SELF参数来删除自己[L4.11(4)]。接下来OSTaskDel()会保证被删除的任务是确实存在的[L4.11(3)]。如果指定的参数是OS_PRIO_SELF的话,这一判断过程(任务是否存在)自然是可以通过的,但笔者不准备为这种情况单独写一段代码,因为这样只会增加代码并延长程序的执行时间。程序清单 L 4.11	删除任务
INT8U OSTaskDel (INT8U prio)
{
OS_TCB   *ptcb;
OS_EVENT *pevent;

if (prio == OS_IDLE_PRIO) {	(1)
return (OS_TASK_DEL_IDLE);
}
if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {	(2)
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) {	(3)
OS_EXIT_CRITICAL();
return (OS_TASK_DEL_ISR);
}
if (prio == OS_PRIO_SELF) {	(4)
Prio = OSTCBCur->OSTCBPrio;
}
if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) {	(5)
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {	(6)
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) {	(7)
if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
pevent->OSEventGrp &= ~ptcb->OSTCBBitY;
}
}
Ptcb->OSTCBDly  = 0;	(8)
Ptcb->OSTCBStat = OS_STAT_RDY;	(9)
OSLockNesting++;	(10)
OS_EXIT_CRITICAL();	(11)
OSDummy();	(12)        OS_ENTER_CRITICAL();
OSLockNesting--;	(13)
OSTaskDelHook(ptcb);	(14)
OSTaskCtr--;
OSTCBPrioTbl[prio] = (OS_TCB *)0;	(15)
if (ptcb->OSTCBPrev == (OS_TCB *)0) {	(16)
ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0;
OSTCBList                  = ptcb->OSTCBNext;
} else {
ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext;
ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev;
}
ptcb->OSTCBNext = OSTCBFreeList;	(17)
OSTCBFreeList   = ptcb;
OS_EXIT_CRITICAL();
OSSched();	(18)
return (OS_NO_ERR);
} else {
OS_EXIT_CRITICAL();
return (OS_TASK_DEL_ERR);
}
}

一旦所有条件都满足了,OS_TCB就会从所有可能的µC/OS-Ⅱ的数据结构中移除。OSTaskDel()分两步完成该移除任务以减少中断响应时间。首先,如果任务处于就绪表中,它会直接被移除[L4.11(6)]。如果任务处于邮箱、消息队列或信号量的等待表中,它就从自己所处的表中被移除[L4.11(7)]。接着,OSTaskDel()将任务的时钟延迟数清零,以确保自己重新允许中断的时候,ISR例程不会使该任务就绪[L4.11(8)]。最后,OSTaskDel()置任务的.OSTCBStat标志为OS_STAT_RDY。注意,OSTaskDel()并不是试图使任务处于就绪状态,而是阻止其它任务或ISR例程让该任务重新开始执行(即避免其它任务或ISR调用OSTaskResume()[L4.11(9)])。这种情况是有可能发生的,因为OSTaskDel()会重新打开中断,而ISR可以让更高优先级的任务处于就绪状态,这就可能会使用户想删除的任务重新开始执行。如果不想置任务的.OSTCBStat标志为OS_STAT_RDY,就只能清除OS_STAT_SUSPEND位了(这样代码可能显得更清楚,更容易理解一些),但这样会使得处理时间稍长一些。
要被删除的任务不会被其它的任务或ISR置于就绪状态,因为该任务已从就绪任务表中删除了,它不是在等待事件的发生,也不是在等待延时期满,不能重新被执行。为了达到删除任务的目的,任务被置于休眠状态。正因为这样,OSTaskDel()必须得阻止任务调度程序[L4.11(10)]在删除过程中切换到其它的任务中去,因为如果当前的任务正在被删除,它不可能被再次调度!接下来,OSTaskDel()重新允许中断以减少中断的响应时间[L4.11(11)]。这样,OSTaskDel()就能处理中断服务了,但由于它增加了OSLockNesting,ISR执行完后会返回到被中断任务,从而继续任务的删除工作。注意OSTaskDel()此时还没有完全完成删除任务的工作,因为它还需要从TCB链中解开OS_TCB,并将OS_TCB返回到空闲OS_TCB表中。
另外需要注意的是,笔者在调用OS_EXIT_CRITICAL()函数后,马上调用了OSDummy() [L4.11(12)],该函数并不会进行任何实质性的工作。这样做只是因为想确保处理器在中断允许的情况下至少执行一个指令。对于许多处理器来说,执行中断允许指令会强制CPU禁止中断直到下个指令结束!Intel 80x86和Zilog Z-80处理器就是如此工作的。开中断后马上关中断就等于从来没开过中断,当然这会增加中断的响应时间。因此调用OSDummy()确保在再次禁止中断之前至少执行了一个调用指令和一个返回指令。当然,用户可以用宏定义将OSDummy()定义为一个空操作指令(译者注:例如MC68HC08指令中的NOP指令),这样调用OSDummy()就等于执行了一个空操作指令,会使OSTaskDel()的执行时间稍微缩短一点。但笔者认为这种宏定义是没价值的,因为它会增加移植µCOS-Ⅱ的工作量。
现在,OSTaskDel()可以继续执行删除任务的操作了。在OSTaskDel()重新关中断后,它通过锁定嵌套计数器(OSLockNesting)减一以重新允许任务调度[L4.11(13)]。接着,OSTaskDel()调用用户自定义的OSTaskDelHook()函数[L4.11(14)],用户可以在这里删除或释放自定义的TCB附加数据域。然后,OSTaskDel()减少µCOS-Ⅱ的任务计数器。OSTaskDel()简单地将指向被删除的任务的OS_TCB的指针指向NULL[L4.11(15)],从而达到将OS_TCB从优先级表中移除的目的。再接着,OSTaskDel()将被删除的任务的OS_TCB从OS_TCB双向链表中移除[L4.11(16)]。注意,没有必要检验ptcb->OSTCBNext==0的情况,因为OSTaskDel()不能删除空闲任务,而空闲任务就处于链表的末端(ptcb->OSTCBNext==0)。接下来,OS_TCB返回到空闲OS_TCB表中,并允许其它任务的建立[L4.11(17)]。最后,调用任务调度程序来查看在OSTaskDel()重新允许中断的时候[L4.11(11)],中断服务子程序是否曾使更高优先级的任务处于就绪状态[L4.11(18)]。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: