中断过程中如何使用堆栈和link寄存器? (ARM 处理器)

How is the stack and link register used in an interrupt procedure? (ARM Processor)

ARM网站说link寄存器存储了子程序、函数调用、异常(如中断)的return信息,那么栈是用来做什么的?

this similar question 的答案说堆栈用于存储 return 地址,而 "push" 的答案是需要放回核心寄存器的局部变量异常之后。

但这就是 link 寄存器的用途,那么为什么需要它呢?两者有什么区别,如何使用?

好的,我想我明白你的问题了。

所以你在函数调用函数中有一些代码

main ()
{
int a,b;
a = myfun0();
b=a+7;
...

所以当我们调用 myfun0() 时 link 寄存器基本上让我们回来所以我们可以做 b = a+7;当然,理解所有这些都被编译为汇编和优化等等,但这足以理解 link 寄存器用于 return 返回到调用之后。

但是如果

myfun0 ()
{
return(myfun1()+3);
}

当 main 调用 myfun0() 时,link 寄存器指向它需要 return 的 main() 函数中的某些代码。然后 myfun0() 调用 myfun1() 它需要返回 myfun0() 在 return 到 main() 之前做更多的数学运算,所以当它调用 myfun1() 时 link 寄存器被设置回来添加 3。问题是当我们将 link 寄存器设置为 return 到 myfun0() 时,我们将需要 return 的 main() 中的地址丢弃。因此,为了防止 IF 该函数将调用另一个函数以及不能全部存在于一次性寄存器中的局部变量, link 寄存器必须放在堆栈上.所以现在 main 调用 myfun0(),myfun0() 将调用一个函数 (myfun1()),因此 link 寄存器的副本(return 地址到 main())被保存在堆栈中. myfun0 调用 myfun1() 如果调用其他东西,myfun1() 遵循相同的规则将 lr 放入堆栈,否则你不必这样做,myfun1() 使用 lr 到 return 到 myfun0(),myfun0() 从堆栈以便它可以 return 到 main。每个函数都遵循这个简单的规则,你不会出错。

现在中断不确定是否相关或者我误解了你的问题。所以 arm 至少为非 cortex-m 内核存储了寄存器。但通常当 interrupt/exception 发生时,如果异常处理程序需要使用前台任务正在使用的任何 resources/registers,则该处理程序需要以前台任务的方式保留堆栈中的那些被打断并不知道会发生这种情况,因为通常中断通常会发生在任何两条指令之间,因此您甚至需要走得更远,以保留在您打断的指令之前由指令设置的标志。

因此将其应用于 arm,您必须查看您正在使用的架构,并查看它在哪里描述了中断过程,您必须保留哪些寄存器,哪些不需要,使用了哪个堆栈指针,等等(一些东西如果您使用的是带有独立中断堆栈和前景堆栈的 arm,则必须在第一个异常之前做好设置。

cortex-m 旨在为您完成 some/all 的工作,它基本上只有一个堆栈,在中断时它会为您推送堆栈上的所有寄存器,因此您可以简单地拥有一个 C 编译函数只是 运行 作为处理程序,硬件会在您之后清理(从保留寄存器的角度来看)。其他一些处理器系列为您做类似的事情,它们可能有一个 return 来自中断指令,与 return 指令分开,其中一个是因为在中断时硬件保存标志和 return地址,但对于简单的调用,您不需要保留标志。

arm 比其他一些指令集灵活得多,有些其他指令集您可能没有任何指令允许您跳转到任何您想要的寄存器中的地址,您可能有限制。您可能会限制将什么寄存器用作堆栈指针,或者堆栈指针本身无法作为通用寄存器访问。按照惯例,手臂中的 sp 是 13,它们允许 push 和 pop 的伪指令转换为正确的 ldmia r13!{blah} 和 stmdb r13!,{blah} 但你可以选择你自己的(如果不使用编译器遵循约定或可以更改开源编译器以使用不同的堆栈指针寄存器)。手臂并不能阻止这一点。 link 寄存器 r14 的魔力无非就是分支 link 或分支 link 交换自动修改 r14,但是指令集允许你使用基本上任何寄存器来 branch/return 用于正常的函数调用。 arm 有足够的通用寄存器来鼓励编译器进行基于寄存器的参数传递与仅堆栈。一些处理器倾向于只传递堆栈参数,并将其 return 地址指令设计为严格基于堆栈,避免将 return 寄存器放在一起,并且如果嵌套函数则必须保存该规则。

所有这些方法都有利弊,在 arm 寄存器传递的情况下是可取的,并且基于寄存器的 return 地址也是如此,但是对于嵌套函数,您必须保留 return 地址在每个嵌套级别都不会迷路。同样,对于中断,您必须按照找到它们的方式将其放回原位,并且能够返回到中断前景的位置。