嵌入式实时操作系统之上下文切换

        好久没有更新博客了,今天想学习一下嵌入式实时操作系统的上下文切换,浅谈一下自己的学习成果,如果有理解不正确的,欢迎各位大神批评指正。下面就以arm-contexM4为例,来介绍一下嵌入式实时操作系统是如何进行上下文切换的。

        所谓上下文切换,其实质就是保存当前线程的地址,压栈,然后对目标线程进行出栈,跳到目标线程执行,在arm-contexM4中上下文切换都是基于pendSv中断进行的,具体源码如下所示:

.global PendSV_Handler
.type PendSV_Handler, %function
PendSV_Handler:
    /* disable interrupt to protect context switch */
    /*关闭中断,保护线程切换*/
    MRS r2, PRIMASK
    CPSID   I

    /* get rt_thread_switch_interrupt_flag */
    /*检查线程切换是否已经处理过,如果为0,则转到pendsv_exit*/
    LDR r0, =rt_thread_switch_interrupt_flag
    LDR r1, [r0]
    CBZ r1, pendsv_exit         /* pendsv already handled */

    /* clear rt_thread_switch_interrupt_flag to 0 */
    /*清pendSV处理过标志为0*/
    MOV r1, #0x00
    STR r1, [r0]
	/*判断来源线程堆栈指针是否为0,如果为0,则直接跳到目标线程切换,如果不为0,则保存from线程上下文*/
    LDR r0, =rt_interrupt_from_thread
    LDR r1, [r0]
    CBZ r1, switch_to_thread    /* skip register save at the first time */
	/*保存from线程堆栈指针*/
    MRS r1, psp                 /* get from thread stack pointer */
    /*对浮点数支持的处理*/
#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    TST     lr, #0x10           /* if(!EXC_RETURN[4]) */
    VSTMDBEQ r1!, {d8 - d15}    /* push FPU register s16~s31 */
#endif
    /*连续压栈form线程堆栈,将R4到R11压入堆栈,先入R11*/
    STMFD   r1!, {r4 - r11}     /* push r4 - r11 register */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    MOV     r4, #0x00           /* flag = 0 */

    TST     lr, #0x10           /* if(!EXC_RETURN[4]) */
    MOVEQ   r4, #0x01           /* flag = 1 */

    STMFD   r1!, {r4}           /* push flag */
#endif
    /*保存堆栈指针*/
    LDR r0, [r0]
    STR r1, [r0]                /* update from thread stack pointer */
/*线程切换*/
switch_to_thread:
	/*加载目标线程的堆栈指针*/
    LDR r1, =rt_interrupt_to_thread
    LDR r1, [r1]
    LDR r1, [r1]                /* load thread stack pointer */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    LDMFD   r1!, {r3}           /* pop flag */
#endif
	/*连续出栈,恢复to线程的R4-R11寄存器值*/
    LDMFD   r1!, {r4 - r11}     /* pop r4 - r11 register */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    CMP     r3,  #0             /* if(flag_r3 != 0) */
    VLDMIANE  r1!, {d8 - d15}   /* pop FPU register s16~s31 */
#endif
	/*更新to线程的堆栈指针*/
    MSR psp, r1                 /* update stack pointer */

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    ORR     lr, lr, #0x10       /* lr |=  (1 << 4), clean FPCA. */
    CMP     r3,  #0             /* if(flag_r3 != 0) */
    BICNE   lr, lr, #0x10       /* lr &= ~(1 << 4), set FPCA. */
#endif

pendsv_exit:
    /* restore interrupt */
    MSR PRIMASK, r2

    ORR lr, lr, #0x04
    /*退出异常处理函数*/
    BX  lr

 那么pendSV中断是如何触发的呢?其实是调度算法获取当前就绪队列中最高优先级的线程后调用上下文切换,触发pendSV中断的,其代码如下:

 /*没有来源的线程上下文切换,通常用于创建线程后第一次调度器启动时调用*/
.global rt_hw_context_switch_to
.type rt_hw_context_switch_to, %function
rt_hw_context_switch_to:
/*R0:目标线程的堆栈指针(线程栈顶指针),将目标线程的堆栈指针存放在rt_interrupt_to_thread中*/
    LDR r1, =rt_interrupt_to_thread
    STR r0, [r1]

#if defined (__VFP_FP__) && !defined(__SOFTFP__)
    /* CLEAR CONTROL.FPCA */
    MRS     r2, CONTROL         /* read */
    BIC     r2, #0x04           /* modify */
    MSR     CONTROL, r2         /* write-back */
#endif

    /* set from thread to 0 */
    /*将来源线程的堆栈设置为0,不保存原始线程堆栈*/
    LDR r1, =rt_interrupt_from_thread
    MOV r0, #0x0
    STR r0, [r1]

    /* set interrupt flag to 1 */
    /*设置在中断里进行线程切换的标志为1,此标志在pendSV中断切换线程后置0*/
    LDR     r1, =rt_thread_switch_interrupt_flag
    MOV     r0, #1
    STR     r0, [r1]

    /* set the PendSV and SysTick exception priority */
    /*设置PendSV和SysTick中断为最低优先级*/
    LDR r0, =NVIC_SYSPRI2
    LDR r1, =NVIC_PENDSV_PRI
    LDR.W   r2, [r0,#0x00]       /* read       */
    ORR     r1,r1,r2             /* modify     */
    STR     r1, [r0]             /* write-back */

	/*触发PendSv中断进行线程切换*/
    LDR r0, =NVIC_INT_CTRL      /* trigger the PendSV exception (causes context switch) */
    LDR r1, =NVIC_PENDSVSET
    STR r1, [r0]

    /* restore MSP */
    /*放弃从芯片启动到第一次上下文切换之前的堆栈内容,将MSP设置为启动时的值*/
    LDR     r0, =SCB_VTOR
    LDR     r0, [r0]
    LDR     r0, [r0]
    NOP
    MSR     msp, r0

	/*使能全局中断,确保pendSv能进行线程切换*/
    /* enable interrupts at processor level */
    CPSIE   F
    CPSIE   I

第二种就是从一个当前线程切换到另外一个线程

rt_hw_context_switch_interrupt:
rt_hw_context_switch:
    /* set rt_thread_switch_interrupt_flag to 1 */
    LDR     r2, =rt_thread_switch_interrupt_flag
    LDR     r3, [r2]
    CMP     r3, #1
    BEQ     _reswitch
    MOV     r3, #1
    STR     r3, [r2]

    LDR     r2, =rt_interrupt_from_thread   /* set rt_interrupt_from_thread */
    STR     r0, [r2]

_reswitch:
    LDR     r2, =rt_interrupt_to_thread     /* set rt_interrupt_to_thread */
    STR     r1, [r2]

    LDR r0, =NVIC_INT_CTRL              /* trigger the PendSV exception (causes context switch) */
    LDR r1, =NVIC_PENDSVSET
    STR r1, [r0]
    BX  LR

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值