浅析μCOS/II v2.85内核OSMutexPend()和OSMutexPost()

 //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;
  }
  //----------------------------------------------------------------------
  //2.OS_EventTaskWait()函数
  void OS_EventTaskWait (OS_EVENT *pevent)
  {
   INT8U y;
  //2007-09-09 gliethttp
  //pevent为此次task挂起的EventPtr单元
   OSTCBCur->OSTCBEventPtr = pevent;
  //清除调度器中该task对应的标志位
   y = OSTCBCur->OSTCBY;
   OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
   if (OSRdyTbl[y] == 0) {
  //当前y行对应的8个或16个task都已经悬停,那么当前y行也清除.
   OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
   }
  //2007-09-09 gliethttp
  //将该task的prio添加到pevent事件控制矩阵中,这个矩阵的功能和OSRdyGrp、OSRdyTbl就绪控制矩阵没有区别
  //都是用来计算出已经就绪tasks中的优先级最高的那个
   pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;
   pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
  }
  //----------------------------------------------------------------------
  //3.OS_EventTOAbort()函数
  void OS_EventTOAbort (OS_EVENT *pevent)
  {
   INT8U y;
  //清除event事件控制矩阵上本task的标志,因为OSTimeTick()函数未清除该单元
  //它仅仅把本task放入就绪控制矩阵,使得本task重新获得被OS调度的机会而已
  //具体的清除工作还要自己完成
  //具体参考《浅析μC/OS-II v2.85内核OSTimeDly()函数工作原理》
   y = OSTCBCur->OSTCBY;
   pevent->OSEventTbl[y] &= ~OSTCBCur->OSTCBBitX;
   if (pevent->OSEventTbl[y] == 0x00) {
   pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
   }
   OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
   OSTCBCur->OSTCBStat = OS_STAT_RDY;
   OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上
  }
  //----------------------------------------------------------------------
  //4.OSMutexPost()函数
  INT8U OSMutexPost (OS_EVENT *pevent)
  {
   INT8U pip;
   INT8U prio;
  #if OS_CRITICAL_METHOD == 3
   OS_CPU_SR cpu_sr = 0;//方式3将把cpsr状态寄存器推入临时堆栈cpu_sr中,可以安全返回之前的中断状态
  #endif
   if (OSIntNesting > 0) {
   return (OS_ERR_POST_ISR);
   }
  #if OS_ARG_CHK_EN > 0
   if (pevent == (OS_EVENT *)0) {
   return (OS_ERR_PEVENT_NULL);
   }
  #endif
   if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {
   return (OS_ERR_EVENT_TYPE);
   }
   OS_ENTER_CRITICAL();
   pip = (INT8U)(pevent->OSEventCnt >> 8);//变相置顶值pip
   prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);//持有mutex资源的task原始优先级
   if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) {
  //因为解决了局部优先级翻转问题,所以OSTCBCur肯定要等于pevent->OSEventPtr
  //否则发生了不知名的异常
   OS_EXIT_CRITICAL();
   return (OS_ERR_NOT_MUTEX_OWNER);
   }
   if (OSTCBCur->OSTCBPrio == pip) {
  //task被提升了优先级到pip置顶值,也就是一个比该task优先级高
  //比pip低的进程需要访问互斥资源,即:存在B角色进程,
  //那么使用OSMutex_RdyAtPrio()把本task从就绪控制矩阵中摘下来
  //同时将自己还原到prio优先级
   OSMutex_RdyAtPrio(OSTCBCur, prio);
   }
   OSTCBPrioTbl[pip] = OS_TCB_RESERVED;
   if (pevent->OSEventGrp != 0) {
   //2007-09-09 gliethttp
   //OS_EventTaskRdy()函数将摘掉等待在pevent事件控制矩阵上的task中优先级最高的task
   //如果该task仅仅等待该pevent事件,那么将该task添加到就绪控制矩阵中
   //OSRdyGrp |= bity;
   //OSRdyTbl[y] |= bitx;这样调度程序就会根据情况调度OS_Sched()该task了
   prio = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
   //保持处于高8位的pip置顶优先级值
   //同时清除低8位数据
   pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
   pevent->OSEventCnt |= prio;//获得执行权的task充当优先级翻转计算中的C角色
   pevent->OSEventPtr = OSTCBPrioTbl[prio];//task的控制块传给OSEventPtr指针,供优先级翻转计算使用
   if (prio <= pip) {
  //2007-09-09 gliethttp
  //prio比pip小,那么说明比置顶值pip优先级还要高的进程竟然访问了共享资源,
  //那么这时可能会出现优先级翻转,因为这时mutex机制已经不起作用,
  //所以应该保证"变相置顶的方式"初始化时,自己内定的最高优先级pip务必大于所有能访问互斥资源的进程优先级[gliethttp]
   OS_EXIT_CRITICAL();
   OS_Sched();//具体参见《浅析μC/OS-II v2.85内核调度函数》
   return (OS_ERR_PIP_LOWER);
   } else {
   OS_EXIT_CRITICAL();
   OS_Sched();//具体参见《浅析μC/OS-II v2.85内核调度函数》
   return (OS_ERR_NONE);
   }
   }
   //没有任何一个task悬停在本event事件控制矩阵上[gliethttp]
   pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;//还原为初始值
   pevent->OSEventPtr = (void *)0;//现在本task不悬停在任何event事件上
   OS_EXIT_CRITICAL();
   return (OS_ERR_NONE);
  }
  //----------------------------------------------------------------------
  //5.OS_EventTaskRdy()函数
  INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg, INT8U msk, INT8U pend_stat)
  {
   OS_TCB *ptcb;
   INT8U x;
   INT8U y;
   INT8U prio;
  #if OS_LOWEST_PRIO <= 63
   INT8U bitx;
   INT8U bity;
  #else
   INT16U bitx;
   INT16U bity;
   INT16U *ptbl;
  #endif
  
  #if OS_LOWEST_PRIO <= 63
  //小于64个task时,快速计算
  //最有优先权的task位于事件控制矩阵中的第y行的第x列
   y = OSUnMapTbl[pevent->OSEventGrp];
   bity = (INT8U)(1 << y);
   x = OSUnMapTbl[pevent->OSEventTbl[y]];
   bitx = (INT8U)(1 << x);
   prio = (INT8U)((y << 3) + x);
  #else
  //对于256个task
  //最有优先权的task位于事件控制矩阵中的第y行的第x列
  //以下的操作原理具体参见《浅析μC/OS-II v2.85内核调度函数》
   if ((pevent->OSEventGrp & 0xFF) != 0) {
   y = OSUnMapTbl[pevent->OSEventGrp & 0xFF];
   } else {
   y = OSUnMapTbl[(pevent->OSEventGrp >> 8) & 0xFF] + 8;
   }
   bity = (INT16U)(1 << y);
   ptbl = &pevent->OSEventTbl[y];
   if ((*ptbl & 0xFF) != 0) {
   x = OSUnMapTbl[*ptbl & 0xFF];
   } else {
   x = OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8;
   }
   bitx = (INT16U)(1 << x);
   prio = (INT8U)((y << 4) + x);//该task对应的prio优先级值
  //ok,等待在event事件上的所有task中,只有在事件控制矩阵中的第y行的第x列task
  //优先级最高、最值的成为此次事件的唤醒对象[gliethttp]
  #endif
  //清除此task在event事件控制矩阵中的bit位
   pevent->OSEventTbl[y] &= ~bitx;
   if (pevent->OSEventTbl[y] == 0) {
   pevent->OSEventGrp &= ~bity;
   }
  //通过prio优先级找到该prio唯一对应的task对应的ptcb进程上下文控制块
   ptcb = OSTCBPrioTbl[prio];
   ptcb->OSTCBDly = 0;//复原为正常
   ptcb->OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上
  #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
   ptcb->OSTCBMsg = pmsg;//传递消息指针
  #else
   pmsg = pmsg;
  #endif
   ptcb->OSTCBStatPend = pend_stat;//悬停状态值
   ptcb->OSTCBStat &= ~msk;//该msk事件已经发生,清除task上下文控制块上的msk位,如:OS_STAT_MUTEX
   if (ptcb->OSTCBStat == OS_STAT_RDY) {
   //如果当前task只是等待该事件,那么把该task放到就绪控制矩阵中,允许内核调度本task
   OSRdyGrp |= bity;
   OSRdyTbl[y] |= bitx;
   }
   return (prio);//返回本task对应的优先级值
  }
  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  PS:"所以从这里来看,os中的各个功能单元管理着自己的事情,就像面向对象的封装一样,"
   "事件控制矩阵和就绪控制矩阵是各个对象独立自治的关键因素"
   "其他对象,都努力说服自己相信别的对象是独立的、可信任的、安全的"[gliethttp]
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值