μC/OS-II---互斥信号量管理1(os_mutex.c)

目录

  • 背景:优先级反转问题
  • 互斥信号量管理
    • 互斥信号量创建
    • 互斥信号量删除
    • 互斥信号量获取/等待

在这里插入图片描述

背景:优先级反转问题

  • 在高优先级任务等待低优先级任务释放资源时,第三个中等优先级任务抢占了低优先级任务。阻塞时间是无法预测的,可能导致高优先级任务无法满足deadline。这是需要解决的问题。μC/OS-II采用的办法:优先级继承协议。【实际采用的方法是由互斥信号量先预占一个优先级】

互斥信号量管理

互斥信号量创建

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);
}

互斥信号量删除

#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

互斥信号量获取/等待

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();
}

你可能感兴趣的:(μC/OS-II学习,c语言)