首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

上下文切换(2)

上下文切换(2)

上下文切换要完成两个操作:保存被中止的线程的上下文;恢复待切入的线程的上下文。在cm3中,上下文切换函数如下:
;函数原型: void__asm_switch_context(struct  thread_vm*new_vm,struct  thread_vm*old_vm);
;-----------------------------------------------------------------------------
__asm_switch_context   PROC
   EXPORT  __asm_switch_context
   mrs    r4,xpsr
         orr         r4,#0x01000000     ;xpsrT标志读不出来,得手工置位。
   push   {r4}               ;保存xpsr
   push   {lr}               ;保存PC,从其他线程切换回来时,相当于原线程调用
                               ;本函数返回,故用lr替代pc
   push   {r0-r3,r12,lr}     ;保存r0-r3,r12,lr
   push   {r4-r11}
   str    sp,[r1]            ;保存旧上下文栈指针到old_vm->stack


   ldr    sp,[r0]            ;取得新上下文指针
   bl     int_restore_asyn_signal ;对应done函数开头的 int_save_asyn_signal 调用
   svc    0
         ENDP
__asm_switch_context函数完成的只是保存上下文,而新线程上下文的切入是在0svc调用中完成的,0svc调用的代码如下:
   add    r0,#32             ;R0保存的是psp指针值
   ldmfd  r0!,{r4-r11}         ;手工弹出r4-r11
   msr    psp,r0             ;psp指向待切入的上下文
   bx     lr                 ;返回,实际弹出的将是带切入上下文
在线程栈中,最底下的8个字是执行svc 0时自动压栈的,我们并不需要,因此第一句直接加32,跳过这8个字。此时,r0指向的是待切入的线程的上下文了。
随后是手工弹出r4-r11,而r0-r3r12lrpcxpsr则是从svc返回时走法律程序自动恢复,因为此时psp已经指向待切入的线程的上下文,故svc将直接返回到新线程,至此,上下文切换完成。
这跟arm7版本不一样,在arm7版本中,是直接手工恢复新线程的上下文,具体可参阅djyos for 44b0的代码。那为什么cm3版本需要这么严格的程序,动用svc才能实现切换呢?问题就出在ICI位上,cm3为了保证中断响应速度,对需要很长执行时间的多寄存器加载指令ldm和多寄存器存储指令stm以及IT指令,可在执行过程中中断,利用ICI为保存执行进度,中断恢复时从被中断处继续执行该指令。然而ICI位是只读的,因此,手工恢复寄存器是不可能恢复这些位的,只有从真正的异常返回才能根据ICI位的状态继续被中断的指令执行。
有人要问了,上下文切换不是通过调用__asm_switch_context函数实现的吗?调用该函数时,不可能正在执行ldmstmIT指令啊?是的,没错,但是,保存上下文除了主动调用上下文切换函数外,还可能是中断。如果在中断服务例程中使得高优先级的线程就绪,中断返回将直接返回到高优先级线程,被中断打断的低优先级线程的上下文就被保留了,直到该线程变成最高优先级线程才被切回。中断在任何时候都有可能发生,当然也可能在正在执行stmldmIT指令时发生,所以,中断保存的上下文,必须模拟异常返回才能确保恢复执行被中断的半截子指令。
3、启动多事件调度

在初始化完成后,切入第一个线程,标志着多事件调度的开始。cm3版本中,初始化程序工作在handler状态,使用msp作为栈指针,而事件在特权状态的thread模式处理,使用psp为栈指针。因此,启动多事件调度要完成以下工作:

a、 切换到特权模式的thread状态。

b、 切入第一个需要处理的事件的线程的上下文。

c、 因初始化函数的任务已经终结,无需保存其上下文。
继承事业,薪火相传
返回列表