您的位置:首页 > 其它

uCOS中任务调度时的上下文切换

2015-06-18 20:08 309 查看
这里以STM32处理器为例,也就是Cortex-M3内核。

所谓的上下文切换呢,就是当 uC/OS转向执行另一个任务的时候,它保存了当前任务的CPU 寄存器到堆栈。并从新任务的的堆栈中 CPU 寄存器载入 CPU。

在这里上下文切换分为两种:一个是任务级的,一个是中断级的。





(1) 开始执行切换,保存状态寄存器和程序指针寄存器到当前的任务堆栈。保存的顺序与中断发生时 CPU 保存寄存器的顺序相同。假定 SR 先入栈,然后其它寄存器入栈。

(2) 当任务被停止时,保存 CPU 的 TSP 到该任务的OS_TCB 中,换句话说,OSTCBCurPtr->StkPtr=R14。

(3) 然后将新任务 OS_TCB 的顶部地址存入 CPU 的TSP 中。换句话说,R14=OSTCBCurPtr->StkPtr。

(4) 最后, 将新任务堆栈中 R13~~R0 关内容载入 CPU寄存器。再然后(此时的会 TSP 指向 PC,如图),通过一个中断返回指令{比如汇编中的 IRET},程序指针寄存器和状态寄存器被恢复到 CPU 的寄存器中。

 

以上是一个大概的流程分析,下面来看看其具体的代码实现过程,当然,这里是用汇编来实现。

 

PendSV_Handler

    CPSID   I                                                   ; Prevent interruption during context switch关所以中断

    MRS     R0, PSP                                             ; PSP is process stack pointer  PSP是当前进程堆栈的指针,将PSP赋值给R0

    CBZ     R0, PendSV_Handler_Nosave                     ; Skip register save the first time如果R0为0时跳转到PendSV_Handler_Nosave

 

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack偏移0x20的位置用来保存R4至R11

    STM     R0, {R4-R11} ;将剩下的R4至R11寄存器保存在此进程的堆栈中

 

    LDR     R1, =p_OSTCBCur                                     ; OSTCBCur->OSTCBStkPtr = SP; 即OSTCBCur->OSTCBStkPtr这个保存当前的栈尾,以便下次弹出

    LDR     R1, [R1]

    STR     R0, [R1]                                            ; R0 is SP of process being switched out

 

                                                                ; At this point, entire context of process has been saved此时,整个上下文的过程已经被保存

PendSV_Handler_Nosave

    PUSH    {R14}                                               ; Save LR exc_return value

    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();  这里的话可以删除的,用于用户扩展呵呵

    BLX     R0

    POP     {R14}

 

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;

    LDR     R1, =OSPrioHighRdy ;将当前优先级变量指向最高优先级

    LDRB    R2, [R1]

    STRB    R2, [R0]

 

    LDR     R0, =p_OSTCBCur                                     ; OSTCBCur  = OSTCBHighRdy;

    LDR     R1, =p_OSTCBHighRdy ;TCB表也一样

    LDR     R2, [R1]

    STR     R2, [R0]

;到这里,[R2]保存的是新的进程的堆栈指针SP

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;  将堆栈指针赋值给SP

    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack 弹出其它寄存器,和前面的是一个逆过程

    ADDS    R0, R0, #0x20 ;和前面的逆过程对比可知

    MSR     PSP, R0                                             ; Load PSP with new process SP将R0中的SP赋值给PSP寄存器

    ORR     LR, LR, #0x04                                       ; Ensure exception retur
bc3a
n uses process stack确保异常返回时使用进程堆栈

    CPSIE   I ;开中断

    BX      LR                                                  ; Exception return will restore remaining context异常返回将恢复那些自动出栈的剩余寄存器

 

晕,排版有点乱,勉强凑合着看吧。。。

可以看出这其实是一个PendSV中断来的,也就是M3的可编程挂起中断,为什么用这个中断来进行上下文切换呢?后面将会进行说明下。

CBZ     R0, PendSV_Handler_Nosave

这句是用来判断是否第一次进入切换,也就是系统刚启动的时候,则直接跳到PendSV_Handler_Nosave这个标号来执行了,因为系统刚启动还没有当前任务嘛。

当不是刚启动的,进行上下文切换,首先是要将当前任务的寄存器保存下来,保存到当前任务的堆栈里,R4-R11手动保存到堆栈,其余会自动保存。

然后

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;

    LDR     R1, =OSPrioHighRdy ;将当前优先级变量指向最高优先级

    LDRB    R2, [R1]

    STRB    R2, [R0]

 

    LDR     R0, =p_OSTCBCur                                     ; OSTCBCur  = OSTCBHighRdy;

    LDR     R1, =p_OSTCBHighRdy ;TCB表也一样

    LDR     R2, [R1]

    STR     R2, [R0]

这里是将优先级最高的,也就是需要切换到的那个任务替换为当前任务。

然后让PSP指向任务堆栈,再然后是一个逆过程,弹出堆栈里的数据到对应的寄存器。

 

上下文切换就差不多这样。。。

关于在M3内核,为什么使用PendSV这个中断来进行上下文切换呢?其原因是这个中断拥有最低优先级的软件中断。

又问,为什么要在拥有最低优先级的软件中断中进行切换呢?这个答案在《Cortex-M3权威指南》第7章 异常   里有详细的答案。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: