一旦所有条件都满足了,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)]。
请求删除任务,OSTaskDelReq()
有时候,如果任务A拥有内存缓冲区或信号量之类的资源,而任务B想删除该任务,这些资源就可能由于没被释放而丢失。在这种情况下,用户可以想法子让拥有这些资源的任务在使用完资源后,先释放资源,再删除自己。用户可以通过OSTaskDelReq()函数来完成该功能。
发出删除任务请求的任务(任务B)和要删除的任务(任务A)都需要调用OSTaskDelReq()函数。任务B的代码如程序清单 L4.12所示。任务B需要决定在怎样的情况下请求删除任务[L4.12(1)]。换句话说,用户的应用程序需要决定在什么样的情况下删除任务。如果任务 需要被删除,可以通过传递被删除任务的优先级来调用OSTaskDelReq()[L4.12(2)]。如果要被删除的任务不存在(即任务已被删除或是还 没被建立),OSTaskDelReq()返回OS_TASK_NOT_EXIST。如果OSTaskDelReq()的返回值为OS_NO_ERR,则 表明请求已被接受但任务还没被删除。用户可能希望任务B等到任务A删除了自己以后才继续进行下面的工作,这时用户可以象笔者一样,通过让任务B延时一定时 间来达到这个目的[L4.12(3)]。笔者延时了一个时钟节拍。如果需要,用户可以延时得更长一些。当任务A完全删除自己后,[L4.12(2)]中的 返回值成为0S_TASK_NOT_EXIST,此时循环结束[L4.12(4)]。 程序清单 L 4.12 请求删除其它任务的任务(任务B)
void RequestorTask (void *pdata)
{
INT8U err;
pdata = pdata;
for (;;) {
/* 应用程序代码 */
if ('TaskToBeDeleted()' 需要被删除) { (1)
while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { (2)
OSTimeDly(1); (3)
}
}
/*应用程序代码*/ (4)
}
}
程序清单 L 4.13 需要删除自己的任务(任务A)
void TaskToBeDeleted (void *pdata)
{
INT8U err;
pdata = pdata;
for (;;) {
/*应用程序代码*/
If (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { (1)
释放所有占用的资源; (2)
释放所有动态内存;
OSTaskDel(OS_PRIO_SELF); (3)
} else {
/*应用程序代码*/
}
}
} |