搜索
 找回密码
 立即注册

简单一步 , 微信登陆

uCOS-II是如何避免调整堆栈指针的

作者:chenfangyu | 时间:2016-12-16 16:12:39 | 阅读:5174| 只看该作者
关于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已经去掉了调整堆栈指针的指令。
收藏
收藏0
分享
分享
点赞
点赞0
反对
反对0
回复

使用道具 举报

大神点评1

沙发#
liugewill 发表于:2017-3-24 11:40:39
好东西,学习了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册
手机版