OSTaskSuspend()
最近的一个程序中用到OSTaskSuspend,并且待挂起的任务中有OSSemPend。结果发现程序运行异常。
当待挂起的任务正在延时或等待事件时,会有何后果?
网上常见的说法
【挂起操作需要被取消,而任务继续等待延时期满,并转入就绪状态。任务可以挂起自己或者其它任务。】
【任务的挂起是可以叠加到其他操作上的。例如,任务被挂起时正在进行延时操作,那么任务的唤醒就需要两个条件:延时的结束以及其他任务的唤醒操作。又如,任务被挂起时正在等待信号量,当任务从信号量的等待队列中清除后也不能立即运行,而必须等到被唤醒后。】
说实话,我有点迷糊。那么从代码入手吧!(只摘录关键代码)
OSTaskSuspend (INT8U prio)
{
...
y = ptcb->OSTCBY;
OSRdyTbl[y] &= ~ptcb->OSTCBBitX;/* Make task not ready */
if (OSRdyTbl[y] == 0x00) {
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
ptcb->OSTCBStat |= OS_STAT_SUSPEND; /* Status of task is 'SUSPENDED' */
...
}
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
...
OS_ENTER_CRITICAL();
if (pevent->OSEventCnt > 0) { /* If sem. is positive, resource available ... */
pevent->OSEventCnt--; /* ... decrement semaphore only if positive. */
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return;
} /* Otherwise, must wait until event occurs */
...
OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next highest priority task ready */
OS_ENTER_CRITICAL();
...
}
void OS_EventTaskWait (OS_EVENT *pevent)
{
...
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
INT8U OSSemPost (OS_EVENT *pevent)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0x00) { /* See if any task waiting for semaphore*/
(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM); /* Ready HPT waiting on event */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find HPT ready to run */
return (OS_NO_ERR);
}
if (pevent->OSEventCnt < 65535u) { /* Make sure semaphore will not overflow */
pevent->OSEventCnt++; /* Increment semaphore count to register event */
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
OS_EXIT_CRITICAL(); /* Semaphore value has reached its maximum */
return (OS_SEM_OVF);
}
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
{
... /* Find HPT waiting for message */
prio = (INT8U)((y << 3) + x); /* Find priority of task getting the msg */
pevent->OSEventTbl[y] &= ~bitx; /* Remove this task from the waiting list */
if (pevent->OSEventTbl[y] == 0x00) {
pevent->OSEventGrp &= ~bity; /* Clr group bit if this was only task pending */
}
...
if (ptcb->OSTCBStat == OS_STAT_RDY) { /* See if task is ready (could be susp'd) */
OSRdyGrp |= bity; /* Put task in the ready to run list */
OSRdyTbl[y] |= bitx;
}
return (prio);
}
----------------------------------------------------------------------------------------------------------------------------显然当TaskA正在pend一个Uart0ReadSem时,pend会调用OS_EventTaskWait(pevent)以Put task in waiting list,即置位pevent->OSEventTbl、pevent->OSEventGrp。
若TaskB将TaskA挂起,对pevent->OSEventTbl、pevent->OSEventGrp无影响,
Uart0接收中断post一个Uart0ReadSem,post判断(pevent->OSEventGrp != 0x00),则不对pevent->OSEventCnt进行+1,而是通过OS_EventTaskRdy将pend在事件上的任务列表中优先级最高的任务移出等待列表。若该任务处于OS_STAT_RDY就将它加入runlist,若处于OS_STAT_SUSPEND则跳过。
-----------------------------------------------------------------------------------------------------------------------------
结论:
TaskB若挂起TaskA,且TaskA正在等待事件sem,那么若发送了sem,TaskA仍能获得这个sem(前提TaskA优先级够高),但需到恢复TaskA时才继续执行。
如此一来,同样等待sem的TaskB就会被TaskA抢走一个sem,造成TaskA虽然被挂起却仍然同TaskB争夺了一次资源。