详解UCOS中的互斥信号量

详解UCOS中的互斥信号量

二值信号量主要用于进行共享资源的独占式访问,比如我们用一个变量来标志一个资源是否可用,当这个变量为1的时候表示资源可用,当这个资源为0的时候表示资源不可用,但是二值信号量容易产生优先级反转,影响系统的实时性。互斥信号量一般用于降解优先级反转,优先级反转就是高优先级的任务的优先级被拉低了。具体如下:
我们有三个任务Task1,Task2,Task3,三个任务的优先级依次降低。
void Task1()
{
    while(1)
    {
        OSSemPend();   //获取信号量
        ......
        OSSemPost();   //释放信号量
    }
}
void Task2()
{
    while(1)
    {
        //注意任务2不需要信号量
    }
}
void Task3()
{
    while(1)
    {
       OSSemPend();   //获取信号量


        OSSemPost();   //释放信号量
    }
}
void main()
{
    OSInit();
    CreateTask(Task1);    //1  最高
    CreateTask(Task2);    //2
    CreateTask(Task3);
    OSStart();

}


如上图所示:在任务2获得信号量的时候,任务1恢复就绪态之后因为没有获得信号量而挂起,所以任务3继续执行,直到任务3执行完毕之后,任务1才开始执行。虽然任务1的优先级最高,但是因为信号量的原因而是任务1的优先级降到任务3的优先级水平。而且任务2加重了优先级反转的程度。

当我们使用了互斥信号量之后,就可以在某种程度上缓解优先级反转的问题了。当高优先级的任务请求互斥信号量时,如果低优先级的任务占有该信号量,则先提升低优先级任务的优先级,使之尽快执行完以释放互斥信号量,这样高优先级的任务也能尽快执行,在某种程度上缓解了优先级反转问题。

使用了互斥信号量之后的运行图如下:

详解UCOS中的互斥信号量_第1张图片

如图所示,在任务3执行的过程中,任务1请求互斥信号量,提升任务3的优先级到最高,使任务3尽快执行完,任务3执行完后释放信号量,任务1开始执行。

UCOS_II中互斥信号量相关的函数主要在os_mutex.c中,核心代码如下:

void  OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
    INT8U      pip;        //继承优先级
    INT8U      mprio;      //现在占有该信号量的优先级
    BOOLEAN    rdy;        //当前任务是否就绪标志
    OS_TCB    *ptcb;
    OS_EVENT  *pevent2;
    INT8U      y;

    OS_ENTER_CRITICAL();
    //该信号量的继承优先级
    pip = (INT8U)(pevent->OSEventCnt >> 8);  

	//该互斥信号量还没有被占用
    if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE)
	{
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
        //直接以当前优先级占用该信号量
        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;     
        pevent->OSEventPtr  = (void *)OSTCBCur;      
        if (OSTCBCur->OSTCBPrio <= pip)
		{
            OS_EXIT_CRITICAL();
            //继承优先级比当前任务的优先级还小
            *err  = OS_ERR_PIP_LOWER;     
        }
		else
		{
            OS_EXIT_CRITICAL();
            //继承优先级至少比当前任务的优先级高
            *err  = OS_NO_ERR;             
        }
        return;
    }

	//占用该信号量的任务的优先级
    mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);

    //现在占有该信号量的任务的TCB
    ptcb  = (OS_TCB *)(pevent->OSEventPtr);

    //占有该信号量的任务的优先级比继承优先级小时才会进行反转
    if (ptcb->OSTCBPrio > pip)
	{
	    //占有该信号量的任务的优先级小于当前任务的优先级进行反转
        if (mprio > OSTCBCur->OSTCBPrio)
		{
		    //从就绪表中移除现在占有该信号量的任务
            y = ptcb->OSTCBY;
            if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0)
            {
                OSRdyTbl[y] &= ~ptcb->OSTCBBitX;
                if (OSRdyTbl[y] == 0)
                {
                    OSRdyGrp &= ~ptcb->OSTCBBitY;
                }
                //占有该信号量的任务已经处于就绪态
                rdy = OS_TRUE;
            }
			else  //占有该信号量的任务现在在系统的等待列表中
			{
			    //占有该信号量的任务的ECB
                pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0)
				{
				    //将占有该信号量的任务从该信号量的等待列表中移除
                    if ((pevent2->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
                        pevent2->OSEventGrp &= ~ptcb->OSTCBBitY;
                    }
                }
                //占有改信号量的任务现在还没有就绪
                rdy = OS_FALSE;		
            }

            //更改占有该信号量的任务的优先级
            ptcb->OSTCBPrio = pip;
            ptcb->OSTCBY    = (INT8U)( ptcb->OSTCBPrio >> 3);
            ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x07);
            ptcb->OSTCBBitY = (INT8U)(1 << ptcb->OSTCBY);
            ptcb->OSTCBBitX = (INT8U)(1 << ptcb->OSTCBX);
            if (rdy == OS_TRUE)
			{
			    //如果之前占有该信号量的任务已近处于就绪态了
			    //就将提升之后的优先级加入到就绪表中
                OSRdyGrp               |= ptcb->OSTCBBitY;
                OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
            }
			else
			{
                pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0)
				{
                   //占有该信号量的任务处于等待列表中就把提升
                   //优先级之后的任务加入到该信号量的等待列表中
                    pevent2->OSEventGrp               |= ptcb->OSTCBBitY;
                    pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                }
            }
            //彻底占用该优先级
            OSTCBPrioTbl[pip] = ptcb;
        }
    }
    OSTCBCur->OSTCBStat   |= OS_STAT_MUTEX;
    OSTCBCur->OSTCBPendTO  = OS_FALSE;
    OSTCBCur->OSTCBDly     = timeout;
    OS_EventTaskWait(pevent);
    OS_EXIT_CRITICAL();
    //重新调度
    OS_Sched();
    OS_ENTER_CRITICAL();
    //该任务等待超时
    if (OSTCBCur->OSTCBPendTO == OS_TRUE)
	{
        OS_EventTO(pevent);
        OS_EXIT_CRITICAL();
        *err = OS_TIMEOUT;
        return;
    }
    OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
    OS_EXIT_CRITICAL();
    *err = OS_NO_ERR;
}





你可能感兴趣的:(嵌入式开发)