Eric_Xue 发表于 2015-10-31 17:30:22

关于ucos2.86运行在cortex-M3上出现bug的原因分析

本帖最后由 Eric_Xue 于 2015-10-31 17:48 编辑

ucos2.86在cortex-M3出现的bug,具体现象,可参考下面的链接。
ucos中的这个问题(BUG),
http://blog.csdn.net/sunlei_telchina/article/details/6205515

2.88修正了bug,下面列出2.88和2.86的不同点。
2.86中的代码是这样的:
   if (OSLockNesting == 0) {                      /* ... scheduler is not locked                  */
          OS_SchedNew();
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy   */
                OSTCBHighRdy = OSTCBPrioTbl;
                OSCtxSwCtr++;                        /* Increment context switch counter             */
                OS_TASK_SW();                        /* Perform a context switch                     */
            }
2.88中的代码是这样的:   
    if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
          OS_SchedNew();
          OSTCBHighRdy = OSTCBPrioTbl;
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy   */
                OSCtxSwCtr++;                        /* Increment context switch counter             */
                OS_TASK_SW();                        /* Perform a context switch                     */
            }
      }
---------------------------------------------------------
原文是这样的

OS_CORE.C:

OSIntExit() and OS_Sched() have changed slightly because of a boundary condition found with the Cortex-M3 port. Specifically, we needed to move the statement:

OSTCBHighRdy = OSTCBPrioTbl;

Before testing for the priority.

-----------------------------------------------
下面开始分析原因          xcj 2015/10/31 15:52

假设有两个任务程序 A 和 B,任务A的优先级比任务B的优先级要高。

正在运行的任务A执行了一个OSTimeDly(1)来延时,OSTimeDly(1)调用OS_Sched()
            
       OS_ENTER_CRITICAL();
      if (OSIntNesting == 0u) {                     
             if (OSLockNesting == 0u) {   
            OS_SchedNew();                   // <----执行完该语句后,OSPrioHighRdy = B,
            if (OSPrioHighRdy != OSPrioCur)   //<------由于OSPrioCur =A,与OSPrioHighRdy不相等
             {
                  OSTCBHighRdy = OSTCBPrioTbl; //<----所以,OSTcbHighRdy = B
                  OSCtxSwCtr++;
                  OS_TASK_SW();//<-- 在Cortex-M3上会触发一个PendSV,在PendSV中执行任务切换。
            }                                 //<----由于中断关闭,所以必须等中断打开才能
      }                                    //<---假设刚好运行到此处发生SysTick中断,但是由于前面已经关闭中断了,所以不会响应SysTick中断。
      }                                       // <------此时,有两个中断在排队了,SysTick中断和PendSV,假设SysTick中断的优先级比PendSV的高。
      OS_ENTER_CRITICAL();// <------一旦执行了该行,意味着立马进入SysTick中断。
   
   记住此时:OSPrioCur          = A ,   OSTCBCur         = A
                        OSPrioHighRdy = B,   OSTCBHighRdy = B

在SysTick中会发现任务A的延时到期了。任务A又会进入就绪表,所以在OSIntExit()调用OS_Sched()时
   
OS_ENTER_CRITICAL();
      if (OSIntNesting == 0u) {                     
             if (OSLockNesting == 0u) {   
             OS_SchedNew();                  <---由于任务A延时到了,所以OSPrioHighRdy = A,
            if (OSPrioHighRdy != OSPrioCur)   <-----而此时OSPrioCur =A,与OSPrioHighRdy相等所以不会执行下面括号里的语句。
             {
                  OSTCBHighRdy = OSTCBPrioTbl;
                  OSCtxSwCtr++;
                  OSIntCtxSw();<-- 该句子不执行,不触发PendSV。
            }      <----此时,OSTCBHighRdy =B                        
      }                                    
      }                                    
      OS_ENTER_CRITICAL();   <-----从SysTick退出后,会立马进入PendSV中断(由A.OSTimeDly(1)触发的)
注意此时:
                     OSPrioCur          = A,   OSTCBCur         = A
                     OSPrioHighRdy = A,   OSTCBHighRdy = B

在PendSV的程序中,只执行任务切换

    OSPrioCur<= OSPrioHighRdy;
    OSTCBCur <= OSTCBHighRdy;
   任务寄存器出栈。
跑到任务程序里面发现:
      OSPrioCur = A,OSTCBCur =B;

也就是说虽然当前运行的实际任务是B,但是当前的优先级却是A。这也就是是为什么最后程序运行到一个低级的程序出不来。高级的程序无法运行的原因。

同样的情况也可能发生在下面这种情况:
(1)正在运行的A调用系统函数SemPend(),semPend()执行完后会OSPrioHighRdy = B, OSTCBHighRdy = B。
(2) 在SemPend()中执行OSched()中过程中发生了外部中断。OSChed()运行完立即进去外部中断程序(而不是PendSV程序)。
(3)而在外部中断中调用了SemPost(),又让A就绪了。执行外部中断的SemPost的OSChed()中会让OSPrioRdy = A,OSTCBHighRdy = B(因为OSPrioHighRdy≠OSPrioCur不成立)
(4)从外部中断后退出,直接进入PendSV中断,PendSV中断直接将 OSPrioCur<= OSPrioHighRdy;    OSTCBCur <= OSTCBHighRdy;
(5) PendSV返回后,实际运行的任务堆栈OSTCBCur =B,而其优先级OSPrioCur =A(任务B占着A的优先级)。

这样任务A永远无法运行(即使任务B延迟自己)。只能让更低优先级任务B运行。

把程序修改一下,使OSTCBHighRdy与OSPrioHighRdy同步更新。就不会出现上述的bug。这里就留给坛友去分析了。

if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
          OS_SchedNew();
          OSTCBHighRdy = OSTCBPrioTbl;
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy   */
                OSCtxSwCtr++;                        /* Increment context switch counter             */
                OS_TASK_SW();                        /* Perform a context switch                     */
            }
      }

如果我有分析错误的地方,还请坛友指正。


374184600 发表于 2015-10-31 19:51:48

学习一下。。。UCOS!

weiwei4 发表于 2015-11-1 07:12:44

分析的很详细,不错

Eric_Xue 发表于 2015-11-2 09:01:57

原文中链接出错,再次发下链接。
ucoii 低版本错误
http://blog.csdn.net/sunlei_telchina/article/details/6205515

hushaoxin 发表于 2015-11-2 11:31:37

楼主分析得非常到位,去年年中,我也是被这个问题折腾得不轻!

ghostcnc 发表于 2016-1-25 12:15:44

学习咯~~~~

zengan007 发表于 2016-1-25 17:16:57

顶一下!!!!!!!!!!!!

fengyunyu 发表于 2016-6-2 16:42:05

LZ分析很到位

sunzhuojun 发表于 2016-6-2 17:12:30


顶一下!!!!!!!!!!!!{:smile:}{:smile:}

armstrong 发表于 2016-6-2 23:14:15

虽然是个老帖子了,但是回味起来依然精彩!顶一下!

MAD_FISH 发表于 2017-4-24 22:49:58

分析透彻{:smile:}

firegod01cn 发表于 2019-7-5 17:28:10

真不错感谢 感谢~~~~~~~~~~~

funnynypd 发表于 2019-7-12 21:29:45

This is great.
页: [1]
查看完整版本: 关于ucos2.86运行在cortex-M3上出现bug的原因分析