首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

μCOS/II 内核OSMutexPend()函数工作原理

μCOS/II 内核OSMutexPend()函数工作原理

//1.OSMutexPend()函数
void OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)
{
     INT8U pip;
     INT8U mprio;
     BOOLEAN rdy;
     OS_TCB *ptcb;
     OS_EVENT *pevent2;
     INT8U y;
     INT8U pend_stat;
#if OS_CRITICAL_METHOD == 3
     OS_CPU_SR cpu_sr = 0;
#endif


#if OS_ARG_CHK_EN > 0
    if (perr == (INT8U *)0) {
        return;
    }
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return;
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {
//确保该event控制块是Mutex类型
        *perr = OS_ERR_EVENT_TYPE;
        return;
    }
    if (OSIntNesting > 0) {
//ISR中,不能使用OSMboxPend()
        *perr = OS_ERR_PEND_ISR;
        return;
    }
    if (OSLockNesting > 0) {
//μCOS/II v2.85内核已经被强制锁住
        *perr = OS_ERR_PEND_LOCKED;
        return;
    }
//非法的统统不是,信号正常,所以有必要进一步处理
     OS_ENTER_CRITICAL();
//在OSMutexCreate()中
//#define OS_MUTEX_AVAILABLE (INT16U)0x00FFu
//pevent->OSEventCnt = (INT16U)((INT16U)prio << 8) | OS_MUTEX_AVAILABLE;
//prio为一个空闲的优先级号,没有task使用该prio[gliethttp]
     pip = (INT8U)(pevent->OSEventCnt >> 8);//变相置顶值pip
    if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {
//当前没有其他进程使用OSMutexPend()占有共享资源,
//所以task将要成为当前唯一占用共享资源的进程,pevent->OSEventCnt的低8位存放
//当前正在使用共享资源task的prio优先级值,
//pip=pevent->OSEventCnt的高8位存放
//"变相置顶"--所谓的最高优先级值pip(访问互斥资源的所有进程的优先级都比该pip低)
//通过提升到这个所谓的顶,来解决优先级翻转问题[gliethttp]
         pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
         pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;
         pevent->OSEventPtr = (void *)OSTCBCur;
        if (OSTCBCur->OSTCBPrio <= pip) {
//因为pip是置顶优先级,所以pip必须是所有能够访问互斥资源的进程中优先级最高的[gliethttp]
//如果低,那么这时mutex互斥机制失效,mutex不起作用了
             OS_EXIT_CRITICAL();
            *perr = OS_ERR_PIP_LOWER;
        } else {
             OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;
        }
        return;
    }
//2007-09-09 gliethttp
//说明已经有一个task占用了共享资源,持有共享互斥锁Mutex了,如下做进一步处理
     mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);
     ptcb = (OS_TCB *)(pevent->OSEventPtr);
//2007-09-09 gliethttp
//[注:未提升优先级之前ptcb->OSTCBPrio等于mprio,提升之后ptcb->OSTCBPrio等于pip(置顶优先级)]
//因为μCOS/II v2.85内核采用"变相置顶的方式"来解决优先级翻转问题,所以pip就是A;
//所以在工程应用上有这样一个限制,能够访问互斥空间的所有进程优先级都要低于这个pip,
//优先级高于pip的进程不能访问,互斥空间,所以这也就要求,我们指定的pip的优先级必须保证,
//大于所有能访问互斥空间的进程,[gilethttp]
//只有低于pip优先级的进程才能够受"资源互斥机制"的保护【这就是μCOS/II v2.85内核优先级翻转的局限之处】
//但是,这在实际的工程应用中是可以接受的.
//优先级A>B>C时,B可能引起优先级翻转,所以下面检测OSTCBCur->OSTCBPrio是否处于B的角色
//如果是,那么需要提升C的优先级,否则不会发生优先级翻转[gliethttp]
    if (ptcb->OSTCBPrio > pip) {
//说明ptcb->OSTCBPrio还没有提升到所谓的最高优先级pip,
//否则ptcb->OSTCBPrio将等于pip[gliethttp]
//如果mprio < OSTCBCur->OSTCBPrio说明,当前持有mutex资源的task处于B的角色
        if (mprio > OSTCBCur->OSTCBPrio) {
//这时OSTCBCur->OSTCBPrio处于B的角色,那么可能会发生优先级翻转,
//因为可能,所以程序肯定会照着最坏的情况考虑,使用"变相置顶的方式",提升当前持有互斥资源的task的优先级
//到达这个所谓的"顶"--pip.
             y = ptcb->OSTCBY;
            if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0) {
//如果A已经在就绪控制矩阵中,那么把A从就绪控制矩阵中摘下[gliethttp]
//具体参考《浅析μCOS/II v2.85内核OSMboxPend()和OSMboxPost()函数工作原理》
                 OSRdyTbl[y] &= ~ptcb->OSTCBBitX;
                if (OSRdyTbl[y] == 0) {
                     OSRdyGrp &= ~ptcb->OSTCBBitY;
                }
                 rdy = OS_TRUE;//task处于就绪状态
            } else {
                 pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0) {
                    if ((pevent2->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
//如果A已经在事件控制矩阵中,那么把A从事件控制矩阵中摘下[gliethttp]
//具体参考《浅析μCOS/II v2.85内核OSMboxPend()和OSMboxPost()函数工作原理》
                         pevent2->OSEventGrp &= ~ptcb->OSTCBBitY;
                    }
                }
                 rdy = OS_FALSE;//task处于事件等待状态
            }
             ptcb->OSTCBPrio = pip;//将task优先级置顶到pip
#if OS_LOWEST_PRIO <= 63
//对于64个tasks配置
//重新就绪控制矩阵和事件控制矩阵中使用到(x,y)值
             ptcb->OSTCBY = (INT8U)( ptcb->OSTCBPrio >> 3);
             ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x07);
             ptcb->OSTCBBitY = (INT8U)(1 << ptcb->OSTCBY);
             ptcb->OSTCBBitX = (INT8U)(1 << ptcb->OSTCBX);
#else
//对于255个tasks配置
//重新计算就绪控制矩阵和事件控制矩阵中使用到(x,y)值
             ptcb->OSTCBY = (INT8U)((ptcb->OSTCBPrio >> 4) & 0xFF);
             ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x0F);
             ptcb->OSTCBBitY = (INT16U)(1 << ptcb->OSTCBY);
             ptcb->OSTCBBitX = (INT16U)(1 << ptcb->OSTCBX);
#endif
            if (rdy == OS_TRUE) {
//提升优先级之前该task处于就绪控制矩阵中,等待cpu调度
                 OSRdyGrp |= ptcb->OSTCBBitY;
                 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
            } else {
//提升优先级之前该task处于事件控制矩阵中,等待事件的到来
//需要说明一点,一个task只有两种状态,
//1.要么在就绪控制矩阵中等待被cpu调度
//2.要么阻塞在事件控制矩阵中[gliethttp]
                 pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0) {
                     pevent2->OSEventGrp |= ptcb->OSTCBBitY;
                     pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                }
            }
             OSTCBPrioTbl[pip] = ptcb;//添加新pip优先级下的TCB
        }
    }
     OSTCBCur->OSTCBStat |= OS_STAT_MUTEX;//是Mutex事件让本task进入悬停等待的
     OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;//假定不是超时,为正常收到信号
//超时,如果timeout=0,那么,本task将一直悬停,仅仅当收到事件触发信号后才重新进入调度队列
     OSTCBCur->OSTCBDly = timeout;
//OS_EventTaskWait()函数实现的功能:
//把本task从就绪控制矩阵中摘下,放到pevent事件专有的进程事件控制矩阵表中.
     OS_EventTaskWait(pevent);
     OS_EXIT_CRITICAL();
//因为本task正在运行,所以本task现在的优先级最高的,现在本task已经将自己从就绪控制矩阵--调度器(x,y)矩形阵列中
//把自己摘掉,所以调度函数OS_Sched()一定会切换到另一个task中执行新task的代码[gliethttp]
     OS_Sched();//具体参见《浅析μC/OS-II v2.85内核调度函数》
     OS_ENTER_CRITICAL();
//2007-09-09 gliethttp
//可能因为OSMutexPend()中指定的timeout已经超时
//[由OSTimeTick()函数把本task重新置入了就绪队列,具体参考《浅析μC/OS-II v2.85内核OSTimeDly()函数工作原理》],
//又或者确实在应用程序的某个地方调用了OSMutexPost(),以下代码将具体解析是有什么引起的:1.超时,2.收到正常信号
    if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) {
//是因为timeout超时,使得本task获得重新执行的机会
         pend_stat = OSTCBCur->OSTCBStatPend;
//清除event事件块上本task的标志
         OS_EventTOAbort(pevent);
         OS_EXIT_CRITICAL();
        switch (pend_stat) {
            case OS_STAT_PEND_TO:
            default:
                 *perr = OS_ERR_TIMEOUT;
                 break;
            case OS_STAT_PEND_ABORT:
                 *perr = OS_ERR_PEND_ABORT;
                 break;
        }
        return;
    }
//由OSMutexPost()抛出正常事件,唤醒了本task,因为在OSMutexPost()时,
//已经将本task在event事件控制矩阵上的对应位清除掉,并且把本task放入了就绪控制矩阵中,
//否则本task也不会执行至此.
     OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
     OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;
}
继承事业,薪火相传
返回列表