2025-10-31

RT-Thread这里,为什么传参是个问题呢?

kcontext把函数的地址,参数等上下文数据都保存在了栈底,然后返回这个上下文的地址。yield( )开始执行后,会跳转到__am_asm_trap这个函数开始执行,__am_asm_trap函数是用汇编写的,它首先会把上下文保存到栈中,然后把栈指针放到$a0寄存器,再调用__am_irq_handle函数,这个函数的参数是上下文指针,实际上就是$a0寄存器。对于yield os,__am_irq_handle会检查是否已经注册用户中断处理函数,这个如果注册了的话,__am_irq_handle会构造一个事件变量,这个变量包含事件的类型,然后调用用户中断处理函数,用户中断处理函数有两个参数:事件和上下文,事件是在__am_irq_handle构造的,上下文是__am_asm_trap传来的栈指针,用户的中断处理函数会返回这个栈指针,然后__am_irq_handle会将这个指针再返回给__am_asm_trap,所以__am_irq_handle和用户中断处理函数都可以改变栈中上下文数据。返回到__am_asm_trap函数之后,该函数会从栈中恢复现场,然后从__am_asm_trap函数返回,至此中断结束。

yield os修改了__am_asm_trap函数的行为,从__am_irq_handle函数返回之后,函数会将栈指针切换到__am_irq_handle返回的上下文结构,这样__am_asm_trap返回的时候就不会跳到yield函数那里,而是到一个新的函数入口开始执行,因为这时候栈指针已经提前指向了将要跳转到的函数的上下文,所以跳转到这个新的函数之后参数都可以通过栈获取。

回到RT-Thread的问题上,rt_hw_stack_init就是yield os中的kcontext函数,只不过还有一个额外的要求:如果函数从tentry返回则需要调用texit函数从C语言的角度来说,通过下面的代码可以实现这个功能

C
<span class="line"><span style="color: #000000">...</span></span>
<span class="line"><span style="color: #795E26">tentry</span>
<span style="color: #000000">();</span></span>
<span class="line"><span style="color: #795E26">texit</span>
<span style="color: #000000">();</span></span>
<span class="line"><span style="color: #000000">...</span></span>
C

讲义中提到“kcontext要求不能从入口返回,因此需要一种新的方式来支持texit的功能。我的疑问是如果没有这个要求,那么旧的方式是什么呢?”不能从入口返回“,我的理解这句话的意思是这个入口函数不能返回,只能不停的调用

也就是讲义中提示的可以构造一个包裹函数,包裹函数的功能就是上面的代码,先调用tentry,然后调用texit。将这个包裹函数作为入口传递给kcontext,因为正常情况下函数不会从tentry返回,如果从tentry返回了,就会继续执行texit函数,而RT-Thread会保证不会从texit返回。