中断函数、函数调用 --- 出栈入栈原理分析
子函数或者中断函数的最后,一般的语句都是BX LR 返回调用点,但是两者的含义略有不同
中断函数
当系统发生中断/异常的时候,CM3 处理器会:
1、入栈:将 8 个寄存器的值压入栈,其中 LR 的值是中断前的值;
2、取向量:从向量表中获取对应中断的 ISR 入口地址;
3、取出MSP,更新到栈指针 SP 中,根据进入中断前的状态更新链接寄存器 LR为EXC_RETURN,更新 PC为中断函数;
入栈就是在进入中断/异常服务程序之前的现场保存,硬件自动将 xPSR、PC、LR、R12、R3、R2、R1、R0 压入栈,如果当中断/异常发生时刻,正在使用 PSP,则压入 PSP;否则压入 MSP;一旦进入 ISR,那就一直使用 MSP;
R0~R3 和 R12 自动入栈了,那其他的 R4~R11 呢, 依据 ARM 的 C 语言标准函数调用约定(AAPCS),如果真的用到了 R4~R11,C语言编译为汇编时,编译器生成代码来入栈它们,因为AAPCS是约束C语言到汇编,如果中断函数是用汇编写的,则需要自己编写汇编代码入栈;
当中断最后调用BX LR把EXC_RETURN送给 PC,CPU检测到向PC中载入的是这个特殊值时,就知道是中断返回,于是做中断返回的动作(与压入动作相反:从堆栈中弹出核心寄存器的值,恢复到线程模式或Handler模式等)
ExceptionEntry()伪代码,描述的就是进中断函数前的操作
子函数
BL调用子函数,LR保存的是调用函数后的下一个指令地址。
调用子函数,编译器知道在哪一行代码中进入子函数,所以C语言编译为汇编代码后,在子函数使用的寄存器,如果父函数也使用了,在父函数调用前压栈,子函数返回前会出栈。所以进入子函数前,是否压栈,压栈什么寄存器,是由编译器决定的,编译器依据AAPCS标准。
中断是随时随刻都可能触发,编译器无法提前判断,所以进入中断前的入栈,和退出中断前的出栈,也是依据AAPCS,不过入栈出栈是硬件自动实现。
子函数最后执行BX LR,相当于MOV LR, PC(PC = LR)
参考资料
《Armv7-M Architecture Reference Manual》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
2017-09-29 Matlab ------ 基本操作