| 
 关于uCOS-II的中断服务程序(ISR)中必须加“OSIntNesting == 1”的原因 ==避免调整堆栈指针 
问题概述: 
   自从uCOS-II 版本 v2.04 以后(不含v2.04),在所有的中断服务程序中,当处理最外层中断时,那么必须 保存好现场之后,必须马上 加上如下判断        if( OSIntNesting == 1){ 
          OSTCBCur->OSTCBStkPtr = 当前的堆栈指针 
       } 
       以上代码的意思是:当处于最外层中断时,保存被中断任务的堆栈指针。 
       注意:是所有的ISR,不论是uCOS-II的系统ISR(在v2.04以后的版本,系统的ISR已经加上了),还是用户的ISR  
   这是做是为了在移植中避免调整堆栈指针,减少移植时所需修改的汇编代码量 
问题来源: 
   出现这个问题的根源是当低优先级的任务被中断,当中断完成后由于有高优先级的任务就绪,则必须调度高优先级的任务原来的低优先
 级任务继续被中断着,但是此时的低优先级任务的堆栈已经被破坏,已不能被调度程序直接调度了,要想被调度而必须调整堆栈指针。如下图 所示的场景:  
 
┏━━━━━━━━┓                                          ┏━━━━━━━━┓ 
┃  低优先级任务  ┃                                          ┃  低优先级任务  ┃  
┗━━━━━━━━┛                                          ┗━━━━━━━━┛  
                   / 在低优先级任务执行过程 
                    /中出现中断,任务被打断 
                     /| 
                     ┏━━━━━━━━┓ 
                     ┃中断服务程序ISR ┃(已经是最外层中断) 
                     ┗━━━━━━━━┛ 
                                        / 
                                         / 中断结束后有高优先级任务出现调 
                                          /度高优先任务,原来被中断的任务 
                                           /| 继续被中断着 
                                            ┏━━━━━━━━┓ 
                                            ┃  高优先级的任务┃ 
                                            ┗━━━━━━━━┛
                           需要调整堆栈指针的场景  
 
问题分析: 
   要想理解加上上面两句的原因,不妨假设有下面场景出现: 
      void MyTask(void) 
      { 
        ... 
        ... 
      } 
      该任务在执行过程中被中断打断,下面是它的服务子程序    void MyISR(void) 
      { 
       保存现场(PUSHA) 
       OSIntEnter(); 
       // 此时的堆栈指针是正确的,再往下就不对了,应该在此处保存用户任务堆栈指针 
              
       OSIntExit();  
       恢复现场(POPA) 
    中断返回 
      }     一般的OSIntExit(),大体如下,不论是哪个版本 
     
      OSIntExit() 
      { 
        OS_ENTER_CRITICAL(); 
       if( OSIntNesting==0 && OSLockNesting == 0 ) { 
           找到目前系统中就绪表中优先级最的任务    
      如果不是当前任务,则调度它执行 
           OSIntCtxSw(); 
        } 
        OS_EXIT_CRITICAL(); 
      } 综上所述,任务调用链如下: 
    
    MyTask --> MyISR --> 
                 ①     OSIntExit --> 
                            ②        OS_ENTER_CRITICAL(); ③ 
                                      OSIntCtxSw();        ④ 
由用户的堆栈内容如下 
   
┃     低端存储器          ┃ 
┣━━━━━━━━━━━━━┫ <━━━━━ 最后调度器调度高优先级任务时 
┃调用OSIntCtxSw的返回地址  ┃<- ④    低优先级任务的堆栈指针指向这里 
┣━━━━━━━━━━━━━┫                                         |          
┃ 由OS_ENTER_CRITICAL而保存┃(这两个返回地址都没有用,它们不会返回,  | 
┃ CPU内容,具体是由中断方式┃ 所以必须将堆栈指针调整回下面            | 
┃ 决定                     ┃<- ③                                    | 
┣━━━━━━━━━━━━━┫                                         | 
┃  调用OSIntExit的返回地址 ┃<- ②                                    | 
┣━━━━━━━━━━━━━┫ <━━━━━ 而真正应该指向这里, <<<-- + 
┃                          ┃ 
┃  被保存的CPU寄存器       ┃/  
┃                          ┃ / 
┣━━━━━━━━━━━━━┫  /① 
┃     中断返回地址         ┃  / 
┣━━━━━━━━━━━━━┫ /     
┃      CPU状态字           ┃/  
┣━━━━━━━━━━━━━┫ 
┃      高端存储器          ┃
  
然而在实际的移植过程中,需要调整的指针偏移量是与编译器相关的,如果想要避免调整,显然一个简单的方法就是在调用OSIntExit之前先把
 堆栈指针保存下来,以后调度该用户任务时,直接从此恢复堆栈指针,而不再管实际的堆栈内容了(因为下面的内容相对于调度程序来说已经 没有用处了)  
综上所述:所有的中断服务程序(ISR),不论是uCOS-II系统的还是用户的中断服务程序,都必须在保存好现场以后必须加上如下两句 
  
   if ( OSIntNesting == 0 ) { 
        OSTCBCur->OSTCBStkPtr = 此时的堆栈指针; 
   }
 当时系统的ISR,自v2.04以后都已经加上了这两句,而用户的ISR则完全由用户来保证加上两句,否则会出现问题,因为此时的OSIntCtwSW已经去掉了调整堆栈指针的指令。  |