目录
背景:优先级反转问题
- 在高优先级任务等待低优先级任务释放资源时,第三个中等优先级任务抢占了低优先级任务。阻塞时间是无法预测的,可能导致高优先级任务无法满足deadline。这是需要解决的问题。μC/OS-II采用的办法:优先级继承协议。【实际采用的方法是由互斥信号量先预占一个优先级】
互斥信号量管理
互斥信号量创建
cpp
OS_EVENT *OSMutexCreate (INT8U prio,
INT8U *perr)
{
OS_EVENT *pevent;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_EVENT *)0);
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE)
{
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_EVENT *)0);
}
#endif
#if OS_ARG_CHK_EN > 0u
if (prio != OS_PRIO_MUTEX_CEIL_DIS)
{
if (prio >= OS_LOWEST_PRIO) /* Validate PCP */
{
*perr = OS_ERR_PRIO_INVALID;
return ((OS_EVENT *)0);
}
}
#endif
if (OSIntNesting > 0u) /* See if called from ISR ... */
{
*perr = OS_ERR_CREATE_ISR; /* ... can't CREATE mutex from an ISR */
return ((OS_EVENT *)0);
}
OS_ENTER_CRITICAL();
if (prio != OS_PRIO_MUTEX_CEIL_DIS)
{
if (OSTCBPrioTbl[prio] != (OS_TCB *)0) /* Mutex priority must not already exist */
{
OS_EXIT_CRITICAL(); /* Task already exist at priority ... */
*perr = OS_ERR_PRIO_EXIST; /* ... ceiling priority */
return ((OS_EVENT *)0);
}
OSTCBPrioTbl[prio] = OS_TCB_RESERVED; /* Reserve the table entry */
}
pevent = OSEventFreeList; /* Get next free event control block */
if (pevent == (OS_EVENT *)0) /* See if an ECB was available */
{
if (prio != OS_PRIO_MUTEX_CEIL_DIS)
{
OSTCBPrioTbl[prio] = (OS_TCB *)0; /* No, Release the table entry */
}
OS_EXIT_CRITICAL();
*perr = OS_ERR_PEVENT_NULL; /* No more event control blocks */
return (pevent);
}
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* Adjust the free list */
OS_EXIT_CRITICAL();
pevent->OSEventType = OS_EVENT_TYPE_MUTEX;
pevent->OSEventCnt = (INT16U) ((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; /* Resource is avail. */
pevent->OSEventPtr = (void *)0; /* No task owning the mutex */
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *) (void *)"?";
#endif
OS_EventWaitListInit (pevent);
*perr = OS_ERR_NONE;
return (pevent);
}
互斥信号量删除
cpp
#if OS_MUTEX_DEL_EN > 0u
OS_EVENT *OSMutexDel (OS_EVENT *pevent,
INT8U opt,
INT8U *perr)
{
BOOLEAN tasks_waiting;
OS_EVENT *pevent_return;
INT8U pcp; /* Priority ceiling priority */
INT8U prio;
OS_TCB *ptcb;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_EVENT *)0);
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0) /* Validate 'pevent' */
{
*perr = OS_ERR_PEVENT_NULL;
return (pevent);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) /* Validate event block type */
{
*perr = OS_ERR_EVENT_TYPE;
return (pevent);
}
if (OSIntNesting > 0u) /* See if called from ISR ... */
{
*perr = OS_ERR_DEL_ISR; /* ... can't DELETE from an ISR */
return (pevent);
}
OS_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0u) /* See if any tasks waiting on mutex */
{
tasks_waiting = OS_TRUE; /* Yes */
}
else
{
tasks_waiting = OS_FALSE; /* No */
}
switch (opt)
{
case OS_DEL_NO_PEND: /* DELETE MUTEX ONLY IF NO TASK WAITING --- */
if (tasks_waiting == OS_FALSE)
{
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *) (void *)"?";
#endif
pcp = (INT8U) (pevent->OSEventCnt >> 8u);
if (pcp != OS_PRIO_MUTEX_CEIL_DIS)
{
OSTCBPrioTbl[pcp] = (OS_TCB *)0; /* Free up the PCP */
}
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */
pevent->OSEventCnt = 0u;
OSEventFreeList = pevent;
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Mutex has been deleted */
}
else
{
OS_EXIT_CRITICAL();
*perr = OS_ERR_TASK_WAITING;
pevent_return = pevent;
}
break;
case OS_DEL_ALWAYS: /* ALWAYS DELETE THE MUTEX ---------------- */
pcp = (INT8U) (pevent->OSEventCnt >> 8u); /* Get PCP of mutex */
if (pcp != OS_PRIO_MUTEX_CEIL_DIS)
{
prio = (INT8U) (pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* Get owner's orig prio */
ptcb = (OS_TCB *)pevent->OSEventPtr;
if (ptcb != (OS_TCB *)0) /* See if any task owns the mutex */
{
if (ptcb->OSTCBPrio == pcp) /* See if original prio was changed */
{
OSMutex_RdyAtPrio (ptcb, prio); /* Yes, Restore the task's original prio */
}
}
}
while (pevent->OSEventGrp != 0u) /* Ready ALL tasks waiting for mutex */
{
(void)OS_EventTaskRdy (pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_ABORT);
}
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *) (void *)"?";
#endif
pcp = (INT8U) (pevent->OSEventCnt >> 8u);
if (pcp != OS_PRIO_MUTEX_CEIL_DIS)
{
OSTCBPrioTbl[pcp] = (OS_TCB *)0; /* Free up the PCP */
}
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */
pevent->OSEventCnt = 0u;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL();
if (tasks_waiting == OS_TRUE) /* Reschedule only if task(s) were waiting */
{
OS_Sched(); /* Find highest priority task ready to run */
}
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Mutex has been deleted */
break;
default:
OS_EXIT_CRITICAL();
*perr = OS_ERR_INVALID_OPT;
pevent_return = pevent;
break;
}
return (pevent_return);
}
#endif
互斥信号量获取/等待
cpp
void OSMutexPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr)
{
INT8U pcp; /* Priority Ceiling Priority (PCP) */
INT8U mprio; /* Mutex owner priority */
BOOLEAN rdy; /* Flag indicating task was ready */
OS_TCB *ptcb;
OS_EVENT *pevent2;
INT8U y;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0) /* Validate 'pevent' */
{
*perr = OS_ERR_PEVENT_NULL;
return;
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) /* Validate event block type */
{
*perr = OS_ERR_EVENT_TYPE;
return;
}
if (OSIntNesting > 0u) /* See if called from ISR ... */
{
*perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return;
}
if (OSLockNesting > 0u) /* See if called with scheduler locked ... */
{
*perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */
return;
}
/*$PAGE*/
OS_ENTER_CRITICAL();
pcp = (INT8U) (pevent->OSEventCnt >> 8u); /* Get PCP from mutex */
/* Is Mutex available? */
if ((INT8U) (pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE)
{
pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; /* Yes, Acquire the resource */
pevent->OSEventCnt |= OSTCBCur->OSTCBPrio; /* Save priority of owning task */
pevent->OSEventPtr = (void *)OSTCBCur; /* Point to owning task's OS_TCB */
if ((pcp != OS_PRIO_MUTEX_CEIL_DIS) &&
(OSTCBCur->OSTCBPrio <= pcp)) /* PCP 'must' have a SMALLER prio ... */
{
OS_EXIT_CRITICAL(); /* ... than current task! */
*perr = OS_ERR_PCP_LOWER;
}
else
{
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
}
return;
}
if (pcp != OS_PRIO_MUTEX_CEIL_DIS)
{
mprio = (INT8U) (pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* Get priority of mutex owner */
ptcb = (OS_TCB *) (pevent->OSEventPtr); /* Point to TCB of mutex owner */
if (ptcb->OSTCBPrio > pcp) /* Need to promote prio of owner?*/
{
if (mprio > OSTCBCur->OSTCBPrio)
{
y = ptcb->OSTCBY;
if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0u) /* See if mutex owner is ready */
{
OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; /* Yes, Remove owner from Rdy ...*/
if (OSRdyTbl[y] == 0u) /* ... list at current prio */
{
OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
}
rdy = OS_TRUE;
}
else
{
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)0) /* Remove from event wait list */
{
y = ptcb->OSTCBY;
pevent2->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;
if (pevent2->OSEventTbl[y] == 0u)
{
pevent2->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
}
}
rdy = OS_FALSE; /* No */
}
ptcb->OSTCBPrio = pcp; /* Change owner task prio to PCP */
#if OS_LOWEST_PRIO <= 63u
ptcb->OSTCBY = (INT8U) ( ptcb->OSTCBPrio >> 3u);
ptcb->OSTCBX = (INT8U) ( ptcb->OSTCBPrio & 0x07u);
#else
ptcb->OSTCBY = (INT8U) ((INT8U) (ptcb->OSTCBPrio >> 4u) & 0xFFu);
ptcb->OSTCBX = (INT8U) ( ptcb->OSTCBPrio & 0x0Fu);
#endif
ptcb->OSTCBBitY = (OS_PRIO) (1uL << ptcb->OSTCBY);
ptcb->OSTCBBitX = (OS_PRIO) (1uL << ptcb->OSTCBX);
if (rdy == OS_TRUE) /* If task was ready at owner's priority ...*/
{
OSRdyGrp |= ptcb->OSTCBBitY; /* ... make it ready at new priority. */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
else
{
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)0) /* Add to event wait list */
{
pevent2->OSEventGrp |= ptcb->OSTCBBitY;
pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
OSTCBPrioTbl[pcp] = ptcb;
}
}
}
OSTCBCur->OSTCBStat |= OS_STAT_MUTEX; /* Mutex not available, pend current task */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout; /* Store timeout in current task's TCB */
OS_EventTaskWait (pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next highest priority task ready */
OS_ENTER_CRITICAL();
switch (OSTCBCur->OSTCBStatPend) /* See if we timed-out or aborted */
{
case OS_STAT_PEND_OK:
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT:
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted getting mutex */
break;
case OS_STAT_PEND_TO:
default:
OS_EventTaskRemove (OSTCBCur, pevent);
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get mutex within TO */
break;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */
#if (OS_EVENT_MULTI_EN > 0u)
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
OS_EXIT_CRITICAL();
}