本帖最后由 john_lee 于 2010-11-22 01:27 编辑
虽然我并不赞同飞船的说法,但我觉得这个CoOS可能有些瑕疵。
这是CoOS中的一个函数(摘录自CoOS源码,本人一字未改):
/**
*******************************************************************************
* @brief Pend for a mail
* @param[in] id Event ID.
* @param[in] timeout The longest time for writting mail.
* @param[out] perr A pointer to error code.
* @retval NULL
* @retval A pointer to mail accept.
*
* @par Description
* @details This function is called to wait for a mail.
* @note
*******************************************************************************
*/
void* CoPendQueueMail(OS_EventID id,U32 timeout,StatusType* perr)
{
P_ECB pecb;
P_QCB pqcb;
P_OSTCB curTCB;
void* pmail;
if(OSIntNesting > 0) /* If the caller is ISR */
{
*perr = E_CALL;
return NULL;
}
#if CFG_PAR_CHECKOUT_EN >0
if(id >= CFG_MAX_EVENT)
{
*perr = E_INVALID_ID; /* Invalid event id,return error */
return NULL;
}
#endif
pecb = &EventTbl[id];
#if CFG_PAR_CHECKOUT_EN >0
if(pecb->eventType != EVENT_TYPE_QUEUE) /* The event type is not queue */
{
*perr = E_INVALID_ID;
return NULL;
}
#endif
if(OSSchedLock != 0) /* Judge schedule is locked or not? */
{
*perr = E_OS_IN_LOCK; /* Schedule is locked,return error */
return NULL;
}
pqcb = (P_QCB)pecb->eventPtr; /* Point at queue control block */
if(pqcb->qSize != 0) /* If there are any messages in the queue */
{
/* Extract oldest message from the queue */
pmail = *(pqcb->qStart + pqcb->head);
pqcb->head++; /* Update the queue head */
pqcb->qSize--; /* Update the number of messages in the queue */
if(pqcb->head == pqcb->qMaxSize)/* Check queue head */
{
pqcb->head = 0;
}
*perr = E_OK;
return pmail; /* Return message received */
}
else /* If there is no message in the queue*/
{
curTCB = TCBRunning;
if(timeout == 0) /* If time-out is not configured */
{
/* Block current task until the event occur */
EventTaskToWait(pecb,curTCB);
/* Have recived message or the queue have been deleted */
pmail = curTCB->pmail;
curTCB->pmail = NULL;
*perr = E_OK;
return pmail; /* Return message received or NULL */
}
else /* If time-out is configured */
{
OsSchedLock();
/* Block current task until event or timeout occurs */
EventTaskToWait(pecb,curTCB);
InsertDelayList(curTCB,timeout);
OsSchedUnlock();
if(curTCB->pmail == NULL) /* If time-out occurred */
{
*perr = E_TIMEOUT;
return NULL;
}
else /* If event occured */
{
pmail = curTCB->pmail;
curTCB->pmail = NULL;
*perr = E_OK;
return pmail; /* Return message received or NULL */
}
}
}
}
这个函数中,pqcb的head, qSize应该属于临界资源,但我没有看到任何保护这些资源的机制。
可以想象一下,一个任务(A)执行到了这一步:假设qSize的当前值为2,当qSize的值从RAM中装载到寄存器后,这时出现了一个中断,这个中断READY了另外一个任务(B),而这个任务(B)又执行了对此队列的post mail动作,在那里,qSize被+1变为了3, 当调度器将CPU重新交给了任务(A)时,任务(A)并不知道qSize在RAM中的值已经发生了变化,而继续用已经装载到寄存器中的旧值2进行-1, 得到了一个值1(当然这是错误的),并且写回到qSize中。
系统将在此后的某个时刻崩溃。 |