rtt进程上下文切换 问题请教

169 views
Skip to first unread message

prife

unread,
Dec 28, 2011, 10:26:34 PM12/28/11
to rt-thread-cnusers
进程上下文切换发生在void rt_schedule(void)中调用
rt_hw_context_switch函数,然后这个函数的最后会触发PendSV调用,但是在schedule的入口先关闭了中断,所以此时并不能直接进入其异常处理程序PendSV_Handler中,所以context_switch会先返回schedule的最后,打开中断之后,才能进入PendSV处理代码,首先硬件完成8个寄存器的压栈动作,然后PendSV_Handler中处理程序完成剩下的r4-r11这个8个寄存器的入栈动作,使用psp更新源线程控制块中的sp域值,然后取出目的线程的sp域,弹出r4-r11, 更新psp,然后BX LR,就会使用目的线程的堆栈中保存的PC来恢复到目的线程原来被打断的地方继续执行。

这个描述木问题吧?

我的问题是,在context_switch中rt_thread_switch_interrupt_flag 这个变量是做什么用的呢? 它的意义是什么?
从变量名上看是用于中断模式下发生进程上下文切换的标志变量,但是为什么需要它呢?


;/*
; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
; * r0 --> from
; * r1 --> to
; */
rt_hw_context_switch_interrupt
    EXPORT rt_hw_context_switch_interrupt
rt_hw_context_switch    PROC
    EXPORT 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
    ENDP

; r0 --> swith from thread stack
; r1 --> swith to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
PendSV_Handler   PROC
    EXPORT PendSV_Handler

    ; disable interrupt to protect context switch
    MRS     r2, PRIMASK
    CPSID   I

    ; get rt_thread_switch_interrupt_flag
    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
    MOV     r1, #0x00
    STR     r1, [r0]

    LDR     r0, =rt_interrupt_from_thread
    LDR     r1, [r0]
    CBZ     r1, swtich_to_thread    ; skip register save at the first time

    MRS     r1, psp                 ; get from thread stack pointer
    STMFD   r1!, {r4 - r11}         ; push r4 - r11 register
    LDR     r0, [r0]
    STR     r1, [r0]                ; update from thread stack pointer

swtich_to_thread
    LDR     r1, =rt_interrupt_to_thread
    LDR     r1, [r1]
    LDR     r1, [r1]                ; load thread stack pointer

    LDMFD   r1!, {r4 - r11}         ; pop r4 - r11 register
    MSR     psp, r1                 ; update stack pointer

pendsv_exit
    ; restore interrupt
    MSR     PRIMASK, r2

    ORR     lr, lr, #0x04
    BX      lr
    ENDP


--
把有限的时间投入到无限的学习中去

prife

unread,
Dec 28, 2011, 10:28:11 PM12/28/11
to rt-thread-cnusers
抱歉,忘了说了,上面针对的BSP是 Cortex M3。stm32的BSP
--
把有限的时间投入到无限的学习中去

bernard

unread,
Dec 28, 2011, 11:05:13 PM12/28/11
to rt-threa...@googlegroups.com
rt_thread_switch_interrupt_flag 这个变量有历史原因,也有cortex-m3上的特殊意义:

rt_thread_switch_interrupt_flag 通常会做为中断服务例程中触发了线程上下文切换的标志。因为RT-thread支持中断嵌套,所以在最后一个中断服务例程退出时,将检查这个变量。如果置1,那么进行线程上下文切换,否则返回被中断的线程。

cortex-m3上,因为pendsv的特殊存在,所以上下文切换过程是先触发一个pendsv异常,然后再进行切换。pendsv的优先级被设置成最低,那么在中断服务例程中如果需要进行上下文切换,也可以去触发一个pendsv异常,并且这个pendsv异常总会在所有中断服务例程都处理完以后被处理。

既然都是pendsv异常切换,所以每次切换 rt_thread_switch_interrupt_flag 这个变量都会被置位。但是cortex-m3的中断处理是复杂的,当一个pendsv被处理时,有可能一个新的中断到来,这个中断优先级会比pendsv异常优先级要高,所以处理器会自动抢占pendsv的上下文,去服务高优先级的中断。根据被抢占时刻的不同, rt_thread_switch_interrupt_flag 变量用于记录是否完成了上下文的切换(其实也意味着pendsv一次处理完全完成)。

中断抢占这块要考虑的情况要多一些(特别是pendsv异常被抢占的地方),这方面ucos 2.x老版本考虑得就不全面了,而RT-thread早在0.3.0正式版的时候就已经解决了这类问题。

rogerz

unread,
Dec 29, 2011, 12:18:21 AM12/29/11
to rt-threa...@googlegroups.com
2011/12/29 bernard <bernar...@gmail.com>

rt_thread_switch_interrupt_flag 这个变量有历史原因,也有cortex-m3上的特殊意义:

rt_thread_switch_interrupt_flag 通常会做为中断服务例程中触发了线程上下文切换的标志。因为RT-thread支持中断嵌套,所以在最后一个中断服务例程退出时,将检查这个变量。如果置1,那么进行线程上下文切换,否则返回被中断的线程。

cortex-m3上,因为pendsv的特殊存在,所以上下文切换过程是先触发一个pendsv异常,然后再进行切换。pendsv的优先级被设置成最低,那么在中断服务例程中如果需要进行上下文切换,也可以去触发一个pendsv异常,并且这个pendsv异常总会在所有中断服务例程都处理完以后被处理。

既然都是pendsv异常切换,所以每次切换 rt_thread_switch_interrupt_flag 这个变量都会被置位。但是cortex-m3的中断处理是复杂的,当一个pendsv被处理时,有可能一个新的中断到来,这个中断优先级会比pendsv异常优先级要高,所以处理器会自动抢占pendsv的上下文,去服务高优先级的中断。根据被抢占时刻的不同, rt_thread_switch_interrupt_flag 变量用于记录是否完成了上下文的切换(其实也意味着pendsv一次处理完全完成)。
那么在cortex-m3上这个标志仅仅是用作记录?即便没有这个标志,高优先级中断结束后,pendsv也能继续完成上下文切换吧?
 ,---.  Rogerz Zhang
( @ @ ) Human, not octopus
 ).-.(  Chase what you love. Let the rest go.
'/|||\` Share > Google+ | Note > Tumblr | Random > twitter 微博
  '|`   AsciiArt < Shimrod(hh)

prife

unread,
Dec 29, 2011, 1:00:08 AM12/29/11
to rt-threa...@googlegroups.com
中断上下文切换.jpg
大家看这个图,如果在中断模式下进行 上下文切换,则实际的切换流程应该是这样吧,
由于在PendSV异常处理代码的入口处,已经禁止了中断,所以一旦开始PendSV异常处理程序,就不会再被中断吧。
PendSV_Handler   PROC
    EXPORT PendSV_Handler

    ; disable interrupt to protect context switch
    MRS     r2, PRIMASK
    CPSID   I

查了一下权威指南,cortex M3存在[晚到中断](9.5节,电子版本145页),在PendSV异常压入8个寄存器的过程中,如果一个更高优先级的中断出现了,则PendSV异常处理被抢占,但此时PendSV处理代码没有被执行啊。硬件自然会跑去执行更高优先级的中断处理函数。rt_thread_switch_interrupt_flag这个变量的值也不会被修改。

总之,在Cortex M3这类硬件上,不会出现PendSV处理代码被执行两次或多次的情况吧?故
rt_thread_switch_interrupt_flag 这个变量是否是无意义的?

--
把有限的时间投入到无限的学习中去
中断上下文切换.jpg

prife

unread,
Dec 29, 2011, 1:08:32 AM12/29/11
to rt-threa...@googlegroups.com
中断上下文切换之2.jpg

来一张注释过的图。
中断上下文切换之2.jpg

bernard

unread,
Dec 29, 2011, 1:25:47 AM12/29/11
to rt-threa...@googlegroups.com
“总之,在Cortex M3这类硬件上,不会出现PendSV处理代码被执行两次或多次的情况吧?”

会被执行两次及两次以上的情况。

pendsv异常处理,进去的时候立刻关闭中断。但是立刻依然会被打断,从而切换到更高优先级中断上。在更高优先级中断服务程序中,如果再触发一个pendsv,那么这个pendsv将排队,当上一个pendsv异常执行完毕时,继续这个排队的pendsv异常。这个在STM32上测试下来是这样的
中断上下文切换.jpg

prife

unread,
Dec 29, 2011, 2:13:12 AM12/29/11
to rt-threa...@googlegroups.com
继续问题.png模拟器测试了一下。在PendSV的处理代码之后再次添加触发PendSV异常的代码
pendsv_exit
    ; restore interrupt
    MSR     PRIMASK, r2
;-----------------------------
    LDR     r0, =NVIC_INT_CTRL ; trigger the PendSV exception (causes context switch)
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]
;-----------------------------
    ORR     lr, lr, #0x04
    BX      lr
    ENDP

模拟器就飞掉了,不过这是直接同级异常重入 ,是《权威指南》中明令禁止的,上面的间接情况不知道是否会如bernard所说PendSV处理会排队处理。(我不会测试)求高手来测试一下。
--
把有限的时间投入到无限的学习中去
中断上下文切换.jpg
继续问题.png

rogerz

unread,
Dec 29, 2011, 2:22:30 AM12/29/11
to rt-threa...@googlegroups.com
在RTOS的设计上应该把schedule过程里的context switch放在PendSV中,这是PendSV的一种典型应用吧。

如果你把schedule放在高优先级中断中,那就该利用rt_thread_switch_interrupt_flag来保证在中断退出后才进行context switch。

个人理解,欢迎指正。

bernard

unread,
Dec 29, 2011, 2:25:56 AM12/29/11
to rt-threa...@googlegroups.com
意思不是重入,而是排队服务。即前一个服务完,继续执行下一个

prife

unread,
Dec 29, 2011, 2:52:38 AM12/29/11
to rt-threa...@googlegroups.com
我明白bernarde的意思。不过连着执行两个PendSV着实有点很奇怪。不过应该是这样的了。
最后一张图了。
中断上下文切换之2.jpg

--
把有限的时间投入到无限的学习中去
中断上下文切换之2.jpg

Hu Kyle

unread,
Dec 29, 2011, 3:02:29 AM12/29/11
to rt-threa...@googlegroups.com
我觉得排队的说法有误导性,因为M3硬件没有中断队列设计,不存在中断排队。
主要原因应该是进入PendSV时硬件就已经清掉了PendSVSet标记,如果此时有中断抢占发生,再置了PendSVSet位,就会在执行完旧的PendSV后再次触发PendSV,形成“排队”现象。
中断上下文切换之2.jpg

hnclcj

unread,
Dec 29, 2011, 4:00:51 AM12/29/11
to rt-thread-cnusers
 
 
 弱弱的问下, 画这个图的软件 是什么 有和谐的没?
 
 
2011-12-29

hnclcj

发件人: Hu Kyle
发送时间: 2011-12-29  16:02:53
收件人: rt-thread-cnusers
抄送:
主题: Re: rtt进程上下文切换 问题请教
中断上下文切换之2(12-29-16-59-39).jpg

Grissiom

unread,
Dec 29, 2011, 4:50:48 AM12/29/11
to rt-threa...@googlegroups.com
矮油,既然看那个 flag 的作用,就看它在哪里被读取了,哪里被写入了就好了么~ grep 了一下,它只有在 libcpu 里才出现,可见其完全是体系相关的(或者是很底层的)。对于 cortex-m3 (小弟暂时只熟悉这个架构),对它的读取在 prife 在第一帖里基本已经都引用了,即跳过取 rt_interrupt_from_thread 的过程,并且在 PendSV 里直接退出。然后对那个 flag 修改的地方只有一处,即在 rt_hw_context_switch_to 里把它设置为1了。而 rt_hw_context_switch_to 是在做第一次进程切换的时候使用的,这时候显然没有  rt_interrupt_from_thread,而且这时候系统很干净, SP 神马的都已经在外面设置好了(rt_system_scheduler_start 函数),所以这时候既不用取旧的线程,也不用进行线程的上下文切换~

熊大说还有历史遗留的作用,别的体系架构还不熟悉还没有来得及看。

-- 
Cheers,
Grissiom

Grissiom

unread,
Dec 29, 2011, 5:01:40 AM12/29/11
to rt-threa...@googlegroups.com
2011/12/29 Grissiom <chaos....@gmail.com>

矮油,既然看那个 flag 的作用,就看它在哪里被读取了,哪里被写入了就好了么~ grep 了一下,它只有在 libcpu 里才出现,可见其完全是体系相关的(或者是很底层的)。对于 cortex-m3 (小弟暂时只熟悉这个架构),对它的读取在 prife 在第一帖里基本已经都引用了,即跳过取 rt_interrupt_from_thread 的过程,并且在 PendSV 里直接退出。然后对那个 flag 修改的地方只有一处,即在 rt_hw_context_switch_to 里把它设置为1了。而 rt_hw_context_switch_to 是在做第一次进程切换的时候使用的,这时候显然没有  rt_interrupt_from_thread,而且这时候系统很干净, SP 神马的都已经在外面设置好了(rt_system_scheduler_start 函数),所以这时候既不用取旧的线程,也不用进行线程的上下文切换~


Oops, 看走眼了,对它的修改还有一处,就是在 rt_hw_context_switch 的一开始,先根据它判断用不用取旧的线程地址,如果用取,则把它设置为1…… 那么也就是说,如果有多个中断例程连续执行了,里面 schedule 了很多次,那么就只会取一次旧的线程地址,新的线程地址会每次更新(就绪的最高优先级线程可能会有变化),只会执行一次 PendSV (?)。
 
熊大说还有历史遗留的作用,别的体系架构还不熟悉还没有来得及看。

-- 
Cheers,
Grissiom



--
Cheers,
Grissiom

Grissiom

unread,
Dec 29, 2011, 5:13:21 AM12/29/11
to rt-threa...@googlegroups.com
2011/12/29 Grissiom <chaos....@gmail.com>

矮油,既然看那个 flag 的作用,就看它在哪里被读取了,哪里被写入了就好了么~ grep 了一下,它只有在 libcpu 里才出现,可见其完全是体系相关的(或者是很底层的)。对于 cortex-m3 (小弟暂时只熟悉这个架构),对它的读取在 prife 在第一帖里基本已经都引用了,即跳过取 rt_interrupt_from_thread 的过程,并且在 PendSV 里直接退出。然后对那个 flag 修改的地方只有一处,即在 rt_hw_context_switch_to 里把它设置为1了。而 rt_hw_context_switch_to 是在做第一次进程切换的时候使用的,这时候显然没有  rt_interrupt_from_thread,而且这时候系统很干净, SP 神马的都已经在外面设置好了(rt_system_scheduler_start 函数),所以这时候既不用取旧的线程,也不用进行线程的上下文切换~


我这一开始也看错了……把 CBZ 理解反了……;( 这里应该是不取旧线程,但是进行一次线程切换~

--
Cheers,
Grissiom

prife

unread,
Dec 29, 2011, 5:54:27 AM12/29/11
to rt-threa...@googlegroups.com
之所以提这个问题,是因为仿真了多次,发现每次在PendSV的处理函数中,rt_thread_switch_interrupt_flag 都是1,也就是说这个变量似乎从来没有在PendSV Handler中起过作用。所以我是想知道为什么需要这个变量。

另外,又仔细考虑了一下,我觉得PendSV异常在硬件入栈期间如果被抢占的话,应该是有问题的。

第一次被更高优先级的中断抢占的PendSV异常,硬件会自动为其设置悬起位,那么就是说,在更高级的中断中再执行Scheduler函数时,调用context_switch,它的结束会第二次触发PendSV异常,可是在硬件的中断状态寄存器中,PendSV的悬起位已经为1,那么再次悬起又有什么意义呢? 所以我觉得bernard说会执行两次PendSV的处理函数是不可能的。
反证法:如果真的可以执行两次的话,如果PendSV被抢占多次,岂不是要在最后一个中断返回之后,执行多次PendSV的处理函数? 但是硬件中断状态寄存器中对于PendSV的悬起位只有一个bit。

我觉得正常的执行流程应该如下图。不过尚缺少实验证明。因为这种在PendSV响应的硬件入栈8个寄存器的过程中出现高优先级中断抢占的情况不好复现。所以上面只是单纯的理论分析。

中断上下文切换之4.jpg

--
把有限的时间投入到无限的学习中去
中断上下文切换之4.jpg

Grissiom

unread,
Dec 29, 2011, 6:08:58 AM12/29/11
to rt-threa...@googlegroups.com
2011/12/29 prife <gop...@gmail.com>

之所以提这个问题,是因为仿真了多次,发现每次在PendSV的处理函数中,rt_thread_switch_interrupt_flag 都是1,也就是说这个变量似乎从来没有在PendSV Handler中起过作用。所以我是想知道为什么需要这个变量。

另外,又仔细考虑了一下,我觉得PendSV异常在硬件入栈期间如果被抢占的话,应该是有问题的。

第一次被更高优先级的中断抢占的PendSV异常,硬件会自动为其设置悬起位,那么就是说,在更高级的中断中再执行Scheduler函数时,调用context_switch,它的结束会第二次触发PendSV异常,可是在硬件的中断状态寄存器中,PendSV的悬起位已经为1,那么再次悬起又有什么意义呢? 所以我觉得bernard说会执行两次PendSV的处理函数是不可能的。
反证法:如果真的可以执行两次的话,如果PendSV被抢占多次,岂不是要在最后一个中断返回之后,执行多次PendSV的处理函数? 但是硬件中断状态寄存器中对于PendSV的悬起位只有一个bit。

我觉得正常的执行流程应该如下图。不过尚缺少实验证明。因为这种在PendSV响应的硬件入栈8个寄存器的过程中出现高优先级中断抢占的情况不好复现。所以上面只是单纯的理论分析。

中断上下文切换之4.jpg



只要是“硬件”入栈 PendSV,那么就说明有 schedule 发生了,这时候被换出的线程被保存在了 from_thread 那个变量里,to_thread 变量存储的是要换进来的线程,并且 flag = 1。如果在硬件入栈的时候发生中断,那么, cortex-m3 会在入栈之后执行高优先级的中断(必然不是 PendSV),而挂起 PendSV。假设在此中断例程中调用了 schedule,那么它看到 flag == 1 了,就不会再去保存旧的线程地址到 from_thread 了,原因很简单,旧的线程始终没变~然后 PendSV 里对 flag 做的跳转保证了 PendSV 只执行一次。

再有,就算是在 cortex-m3 里 PendSV 不会被引发好多次,也不能保证在别的体系结构里也有相同的结果。所以这个标志变量怎样都是有用的~
 
在 2011年12月29日 下午5:50,Grissiom <chaos....@gmail.com>写道:

矮油,既然看那个 flag 的作用,就看它在哪里被读取了,哪里被写入了就好了么~ grep 了一下,它只有在 libcpu 里才出现,可见其完全是体系相关的(或者是很底层的)。对于 cortex-m3 (小弟暂时只熟悉这个架构),对它的读取在 prife 在第一帖里基本已经都引用了,即跳过取 rt_interrupt_from_thread 的过程,并且在 PendSV 里直接退出。然后对那个 flag 修改的地方只有一处,即在 rt_hw_context_switch_to 里把它设置为1了。而 rt_hw_context_switch_to 是在做第一次进程切换的时候使用的,这时候显然没有  rt_interrupt_from_thread,而且这时候系统很干净, SP 神马的都已经在外面设置好了(rt_system_scheduler_start 函数),所以这时候既不用取旧的线程,也不用进行线程的上下文切换~

熊大说还有历史遗留的作用,别的体系架构还不熟悉还没有来得及看。

-- 
Cheers,
Grissiom



--
把有限的时间投入到无限的学习中去



--
Cheers,
Grissiom
中断上下文切换之4.jpg

prife

unread,
Dec 29, 2011, 6:39:26 AM12/29/11
to rt-threa...@googlegroups.com
只要是“硬件”入栈 PendSV,那么就说明有 schedule 发生了,这时候被换出的线程被保存在了 from_thread 那个变量里,to_thread 变量存储的是要换进来的线程,并且 flag = 1。如果在硬件入栈的时候发生中断,那么, cortex-m3 会在入栈之后执行高优先级的中断(必然不是 PendSV),而挂起 PendSV。假设在此中断例程中调用了 schedule,那么它看到 flag == 1 了,就不会再去保存旧的线程地址到 from_thread 了,原因很简单,旧的线程始终没变~然后 PendSV 里对 flag 做的跳转保证了 PendSV 只执行一次。
再有,就算是在 cortex-m3 里 PendSV 不会被引发好多次,也不能保证在别的体系结构里也有相同的结果。所以这个标志变量怎样都是有用的~

你仔细看一下我发的图。这个地方你的理解有误。

在高级中断中调用schedule之后,注意(schedule中调用context_switch函数过程中,中断/异常都是被禁止的,
if (rt_interrupt_nest == 0)
{
rt_hw_context_switch((rt_uint32_t)&from_thread->sp, (rt_uint32_t)&to_thread->sp);
}
else
{
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));

rt_hw_context_switch_interrupt((rt_uint32_t)&from_thread->sp, (rt_uint32_t)&to_thread->sp);
}
}
}

/* enable interrupt */
rt_hw_interrupt_enable(level);

所以 context_switch只是中只是触发PendSV(即相当于设置中断标志位),但是实际的PendSV异常却不是从这个context_switch之后立刻发生的,并且第二次高级中断返回之后,实际上高级中断处理函数会完整的执行完毕以后,才会执行PendSV的异常处理程序了但是问题是,这个异常处理是对应的是ISR1中的context_switch还是ISR2中的 context_switch。

prife

unread,
Dec 29, 2011, 6:43:17 AM12/29/11
to rt-threa...@googlegroups.com
我的疑问主要是在 Cortex M3类硬件上,这个变量是否有意义。
你可以从头看一下整个讨论的过程。请仔细看一下我画的图。


--
把有限的时间投入到无限的学习中去

prife

unread,
Dec 29, 2011, 6:51:33 AM12/29/11
to rt-threa...@googlegroups.com
另外,假如(我是说假如PendSV异常真的可以连续执行多次的话),我当然理解 rt_thread_switch_interrupt_flag这个变量的含义的啦。所以我不是不明白此变量可以使后面的PendSV异常中直接跳过,剩下上下文切换作用。

实际上,我觉得PendSV在硬件入栈阶段真的被抢占的话。并且在被抢占的代码中再次执行schedule函数的话,那么当这个中断也执行完毕以后,最终只会执行一次PendSV异常的。故此变量在cortex M3上没有意义,这就是我的疑问

--
把有限的时间投入到无限的学习中去

rogerz

unread,
Dec 29, 2011, 7:36:05 AM12/29/11
to rt-threa...@googlegroups.com
2011/12/29 prife <gop...@gmail.com>

我的疑问主要是在 Cortex M3类硬件上,这个变量是否有意义。
你可以从头看一下整个讨论的过程。请仔细看一下我画的图。

在某处理器(v850)中,这个变量是这么用的

void rt_hw_context_switch(rt_uint32_t from, rt_uint32_t to)
{
    rt_interrupt_from_thread = from;
    rt_interrupt_to_thread = to;
    asm("INT #0");    
}

void rt_hw_context_switch_interrupt(rt_uint32_t from, rt_uint32_t to)
{
    if (rt_thread_switch_interrupt_flag != 1)
    {
        rt_thread_switch_interrupt_flag = 1;
        rt_interrupt_from_thread = from;        
    }
    rt_interrupt_to_thread = to;  
而在cortex-m3上,rt_hw_context_switch和rt_hw_context_switch_interrupt是同一个函数。反汇编成伪代码的话基本上就是后者,加上一句set PENDSVSET_BIT,以便在中断处理结束时调用PendSV Handler。
从这里看,Grissiom有一句说的是对的,在中断中再次发生schedule的话,无需重复设置rt_interrupt_from_thread。

然后看PendSV Handler,反汇编成伪代码的话
disable interrupt;
if (rt_thread_switch_interrupt_flag == 0)
    goto pendsv_exit;   
/* 如果此时被中断,会使下一个PendSV handler进来时看到rt_thread_switch_interrupt_flag == 0 
 * 因为在schedule中rt_thread_switch_interrupt_flag 被置为1,执行结束后又被下一句置为0
 * 这种情况下,该schedule实际上被跳过了
 */
rt_thread_switch_interrupt_flag = 0;
if (rt_interrupt_from_thread == 0)
    goto switch_to_thread
save rt_interrupt_from_thread current context
switch_to_thread:
    load rt_interrupt_to_thread context
pendsv_exit:
    enable interrupt
我猜一进入PendSV handler,PENDSVSET_BIT应该自动被清了。

然后开始判断rt_thread_switch_interrupt_flag,如果为0,说明有另外一个PendSV handler正在执行,此时直接退出,不进行context switch,否则,清 rt_thread_switch_interrupt_flag,开始context switch。
此过程中,如果PENDSVSET_BIT和rt_thread_switch_interrupt_flag再次被rt_hw_context_switch_interrupt置位的话,在当前PendSV handler执行结束后,将执行第二次PendSV handler,即switch到新的thread,这种情况下无需保存from_thread,因为已经保存过了。
实际上rt_hw_context_switch_interrupt里先置rt_thread_switch_interrupt_flag,后置PENDSVSET_BIT,所以进PendSV handler时,rt_thread_switch_interrupt_flag始终为1。
碰到rt_thread_switch_interrupt_flag为0,当且仅当上面注释里的情况发生,此时第二次switch会被跳过。

rogerz

unread,
Dec 29, 2011, 7:48:03 AM12/29/11
to rt-threa...@googlegroups.com
说反了,修正一下……

2011/12/29 rogerz <rogerz...@gmail.com>
    goto pendsv_exit
save rt_interrupt_from_thread current context
switch_to_thread:
    load rt_interrupt_to_thread context
pendsv_exit:
    enable interrupt
我猜一进入PendSV handler,PENDSVSET_BIT应该自动被清了。

然后开始判断rt_thread_switch_interrupt_flag,如果为0,说明有另外一个PendSV handler正在执行,此时直接退出,不进行context switch,否则,清 rt_thread_switch_interrupt_flag,开始context switch。
此过程中,如果PENDSVSET_BIT和rt_thread_switch_interrupt_flag再次被rt_hw_context_switch_interrupt置位的话,在当前PendSV handler执行结束后,将执行第二次PendSV handler,即switch到新的thread。rt_hw_context_switch_interrupt如果看到rt_thread_switch_interrupt_flag为1的话,说明还有另外一个PendSV handler没被执行,所以就跳过修改rt_interrupt_from_thread。

bernard

unread,
Dec 29, 2011, 8:01:31 AM12/29/11
to rt-threa...@googlegroups.com
很热的讨论啊,说说pendsv中中断排队的问题。

代码中,pendsv处理是进入到pendsv服务例程“立刻”进行关闭中断。在cortex-m3上,硬件压栈一些寄存器,那么把这个过程放大:
硬件压栈寄存器,    ,然后关闭中断。

两个,,之间,由于有高优先级中断触发,那么势必要进入高优先级中断服务例程,如果服务例程中再行触发了pendsv异常,这个新的pendsv异常是处于pending状态还是丢弃掉?STM32上实际测试是,排队(或者按照gzhuli的说法,是pending)。

prife

unread,
Dec 29, 2011, 8:05:03 AM12/29/11
to rt-threa...@googlegroups.com
问题是排到哪里去? 没地排啊。 
每个中断就俩位
1)活动状态位
2)悬起位

怎么排队呢?
--
把有限的时间投入到无限的学习中去

bernard

unread,
Dec 29, 2011, 8:09:37 AM12/29/11
to rt-threa...@googlegroups.com
其实不是排啊,是进入第一个pendsv时相应的位已经清除了。而新的pendsv触发时,相应的位被置位,也就是pending住了。

吴洋勇

unread,
Dec 29, 2011, 8:09:53 AM12/29/11
to rt-threa...@googlegroups.com
当某中断的服务例程开始执行时,就称此中断进入了“活跃”状态,并且其悬起位会被硬件自动清除,如图7.10所示。在一个中断活跃后,直到其服务例程执行完毕,并且返回(亦称为中断退出,第九章详细讨论)后,才能对该中断的新请求予以响应(单实例)。当然,新请求在得到响应时,亦是由硬件自动清零其悬起标志位。中断服务例程也可以在执行过程中把自己对应的中断重新悬起(使用时要注意避免进入“死循环”——译注)。


如果中断源咬住请求信号不放,该中断就会在其上次服务例程返回后再次被置为悬起状态,如图7.11所示。这一点上CM3和传统的ARM7TDMI是相同的。


在 2011年12月29日 下午9:01,bernard <bernar...@gmail.com>写道:



--
吴洋勇

吴洋勇

unread,
Dec 29, 2011, 8:13:24 AM12/29/11
to rt-threa...@googlegroups.com
我的图呢!


; r0 --> swith from thread stack
; r1 --> swith to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
PendSV_Handler   PROC
    EXPORT PendSV_Handler

    ; disable interrupt to protect context switch
    // 这里已经进入 handler 状态,“悬起位会被硬件自动清除”
    MRS     r2, PRIMASK
    //  如果中断关闭之前,中断源咬住请求信号不放,该中断就会在其上次服务例程返回后再次被置为悬起状态

    CPSID   I

    ; get rt_thread_switch_interrupt_flag
    LDR     r0, =rt_thread_switch_interrupt_flag
    LDR     r1, [r0]
    CBZ     r1, pendsv_exit         ; pendsv already handled


--
吴洋勇

MingBai

unread,
Dec 29, 2011, 8:24:41 AM12/29/11
to rt-threa...@googlegroups.com
���ǵ�ͼ����ô˧
������ɶ���ģ�

�� 2011/12/29 21:13, ������ �:
�ҵ�ͼ�أ�


; r0 --> swith from thread stack
; r1 --> swith to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
PendSV_Handler   PROC
    EXPORT PendSV_Handler

    ; disable interrupt to protect context switch
    // �����Ѿ����� handler ״̬��������λ�ᱻӲ���Զ����
    MRS     r2, PRIMASK
    //  ����жϹر�֮ǰ���ж�Դҧס�����źŲ��ţ����жϾͻ������ϴη�����̷��غ��ٴα���Ϊ����״̬

    CPSID   I

    ; get rt_thread_switch_interrupt_flag
    LDR     r0, =rt_thread_switch_interrupt_flag
    LDR     r1, [r0]
    CBZ     r1, pendsv_exit         ; pendsv already handled


�� 2011��12��29�� ����9:09�������� <wuyan...@gmail.com>д ����
��ij�жϵķ�����̿�ʼִ ��ʱ���ͳƴ��жϽ����ˡ���Ծ��״̬������������λ�ᱻӲ���Զ������ͼ7.10��ʾ����һ���жϻ�Ծ��ֱ����������ִ���� �ϣ����ҷ��أ����Ϊ�ж��˳����ھ�����ϸ���ۣ��󣬲��ܶԸ��жϵ�������������Ӧ����ʵ���Ȼ���������ڵõ���Ӧʱ��������Ӳ ���Զ������������־λ���жϷ������Ҳ������ִ�й���а��Լ���Ӧ���ж���������ʹ��ʱҪע�������롰��ѭ����������ע����


����ж�Դҧס�����źŲ��ţ����жϾͻ������ϴη�����̷��غ��ٴα���Ϊ����״̬����ͼ7.11��ʾ����һ����CM3�ʹ�ͳ�� ARM7TDMI����ͬ�ġ�


�� 2011��12��29�� ����9:01��bernard <bernar...@gmail.com>д ����
���ȵ����۰���˵˵pendsv���ж��Ŷӵ����⡣

�����У�pendsv�����ǽ��뵽pendsv������̡����̡����йر��жϡ���cortex-m3�ϣ�Ӳ��ѹջһЩ�Ĵ� ������ô�������̷Ŵ�
Ӳ��ѹջ�Ĵ�����    ��Ȼ��ر��жϡ�

��������֮�䣬�����и����ȼ��жϴ�������ô�Ʊ�Ҫ��������ȼ��жϷ�����̣���������������д�����pendsv�쳣�� ����µ�pendsv�쳣�Ǵ���pending״̬���Ƕ������STM32��ʵ�ʲ����ǣ��Ŷӣ����߰���gzhuli��˵ ������pending����

�� 2011��12��29�� ����8:48��rogerz <rogerz...@gmail.com>д ����

˵���ˣ�����һ�¡���

2011/12/29 rogerz <rogerz...@gmail.com>
2011/12/29 prife <gop...@gmail.com>
�ҵ�������Ҫ���� Cortex M3��Ӳ���ϣ���������Ƿ������塣
����Դ�ͷ��һ��������۵Ĺ�̡�����ϸ��һ���һ���ͼ��

��ij������(v850)�У������������ô�õ�

void rt_hw_context_switch(rt_uint32_t from, rt_uint32_t to)
{
    rt_interrupt_from_thread = from;
    rt_interrupt_to_thread = to;
    asm("INT #0");    
}

void rt_hw_context_switch_interrupt(rt_uint32_t from, rt_uint32_t to)
{
    if (rt_thread_switch_interrupt_flag != 1)
    {
        rt_thread_switch_interrupt_flag = 1;
        rt_interrupt_from_thread = from;        
    }
    rt_interrupt_to_thread = to;  
����cortex-m3�ϣ�rt_hw_context_switch��rt_hw_context_switch_interrupt��ͬһ���������α����Ļ����Ͼ��Ǻ��ߣ�����һ��set PENDSVSET_BIT���Ա����жϴ������ʱ����PendSV Handler��
�����￴��Grissiom��һ��˵���ǶԵģ����ж����ٴη���schedule�Ļ��������ظ�����rt_interrupt_from_thread��
Ȼ��PendSV Handler��������α����Ļ�
disable interrupt;
if (rt_thread_switch_interrupt_flag == 0)
    goto pendsv_exit;   
/* ����ʱ���жϣ���ʹ��һ��PendSV handler����ʱ����rt_thread_switch_interrupt_flag == 0 
 * ��Ϊ��schedule��rt_thread_switch_interrupt_flag ����Ϊ1��ִ�н�����ֱ���һ����Ϊ0
 * ��������£���scheduleʵ���ϱ������
 */
rt_thread_switch_interrupt_flag = 0;
if (rt_interrupt_from_thread == 0)
    goto pendsv_exit
save rt_interrupt_from_thread current context
switch_to_thread:
    load rt_interrupt_to_thread context
pendsv_exit:
    enable interrupt
�Ҳ�һ����PendSV handler��PENDSVSET_BITӦ���Զ������ˡ�
Ȼ��ʼ�ж�rt_thread_switch_interrupt_flag�����Ϊ0��˵��������һ��PendSV handler����ִ�У���ʱֱ���˳���������context switch�������� rt_thread_switch_interrupt_flag����ʼcontext switch��
�˹���У����PENDSVSET_BIT��rt_thread_switch_interrupt_flag�ٴα�rt_hw_context_switch_interrupt��λ�Ļ����ڵ�ǰPendSV handlerִ�н���󣬽�ִ�еڶ���PendSV handler����switch���µ�thread��rt_hw_context_switch_interrupt���rt_thread_switch_interrupt_flagΪ1�Ļ���˵����������һ��PendSV handlerû��ִ�У����Ծ�����޸�rt_interrupt_from_thread��
ʵ����rt_hw_context_switch_interrupt������rt_thread_switch_interrupt_flag������PENDSVSET_BIT�����Խ�PendSV handlerʱ��rt_thread_switch_interrupt_flagʼ��Ϊ1��
����rt_thread_switch_interrupt_flagΪ0�����ҽ�������ע�������������ʱ�ڶ���switch�ᱻ���
  ,---.  Rogerz Zhang

( @ @ ) Human, not octopus
 ).-.(  Chase what you love. Let the rest go.

'/|||\` Share > Google+ | Note > Tumblr | Random > twitter ΢��
  '|`   AsciiArt < Shimrod(hh)





--
������



--
������

prife

unread,
Dec 29, 2011, 8:26:52 AM12/29/11
to rt-threa...@googlegroups.com


在 2011年12月29日 下午9:24,MingBai <mbb...@gmail.com>写道:
你们的图都这么帅
都是用啥画的?

我用smartdraw2010画的。 我觉得还是权威指南的图画的好,不知道译者用啥画的。visio么? 

--
把有限的时间投入到无限的学习中去

rogerz

unread,
Dec 29, 2011, 8:35:19 AM12/29/11
to rt-threa...@googlegroups.com
2011/12/29 prife <gop...@gmail.com>



在 2011年12月29日 下午9:24,MingBai <mbb...@gmail.com>写道:
你们的图都这么帅
都是用啥画的?

我用smartdraw2010画的。 我觉得还是权威指南的图画的好,不知道译者用啥画的。visio么? 

我觉得要开始跑题了 ……
smartdraw和visio都不便宜。以后可以找找在线的免费服务,更方便分享讨论。

Grissiom

unread,
Dec 29, 2011, 9:35:39 AM12/29/11
to rt-threa...@googlegroups.com
2011/12/29 prife <gop...@gmail.com>

嗯,PendSV 处理的即不一定是 ISR1 中的 context_swtich 也不一定是 ISR2 中的 context_switch,而是~,context_switch ;)

我们把 context_switch 分为两个部分,即,1,将旧的线程切出;2,将新的进程切入。

是第一个 ISR,它是知道被中断的是哪一个线程的,他里面的 schedule 会完成这么几个个任务:
1,计算最高优先级的就绪进程(这个prife 已经很清楚了)
2,存储旧的进程(rt_current_thread)到 from_thread。
3,把 rt_current_thread 设置为那个算出来的线程,注意,这时候 current_thread 就不是被中断的那个线程了。
4,执行 hw_context_switch ,它的两个参数是旧进程和新进程。
     5,设置 flag,把旧进程和新的进程分别存到 全局变量 rt_interrupt_from_thread 和 rt_interrupt_to_thread 里
     6,pending PendSV


第二个 ISR 里的 schedule 则是这样的:
1,计算
2,存储旧进程 rt_current_thread 到 from_thread,注意,这时的 current_thread 已经不是之前被中断掉的那个线程了。
3,设置新的 current_thread,这个是新计算出来的结果,是最新的最高优先级就绪线程。
4,依然会把旧线程(ISR1 里算出来的那个)和新线程传递给 hw_context_switch
      5,看到 flag == 1 了,知道之前设置过旧进程了,所以跳过了对 全局变量 旧线程的覆盖,保留了那个真的被中断掉的线程,并且更新新线程 rt_interrupt_to_thread
      6,pending PendSV

然后真正的 PendSV 就会切出 ISR1 里设置的真正的旧线程,并切入 ISR2 里设置的新的最高优先级就绪线程。

所以上下文切换是分为两个部分的,而这个 flag 正是 上半部分是否完成的标志。这大概是哪个体系架构都无法逃避的一个变量。

所以熊大的放大方法或许还可以放的更大:

schedule,……,PendSV


(呼,写完之后,自己注意到之前没有注意到的很多细节,茅塞顿开啊……)

--
Cheers,
Grissiom
Reply all
Reply to author
Forward
0 new messages