堆栈寄存器(SP)的工作原理详解
堆栈寄存器(Stack Pointer, SP)是处理器中一个专门用于管理栈(Stack)的寄存器,它保存了当前栈的顶部地址。堆栈寄存器在函数调用、返回、局部变量存取、中断处理等场景中起着关键作用。
堆栈区域对应CPU的内存映射在如图所示的位置↓
栈(Stack)和堆栈寄存器的基本概念
栈(Stack): 栈是一种后进先出(LIFO) 的数据结构。在计算机中,栈通常用于:
- 保存函数调用的返回地址。
- 保存函数的局部变量。
- 保存中断或异常处理的上下文。
栈通常位于内存的高地址部分,栈的增长方向取决于架构:
- 向下增长:从高地址向低地址扩展(多数架构采用),我们下面的例子使用的是
Linux X86
的系统作为示例演示。 - 向上增长:从低地址向高地址扩展。
堆栈指针(SP, Stack Pointer)
堆栈寄存器保存当前栈顶的地址。
当数据被压入栈(Push) 时,SP会更新指向新的栈顶地址。
当数据从栈中弹出(Pop) 时,SP会恢复到之前的栈顶地址。
如上图所示:
堆栈寄存器的工作原理
堆栈寄存器的核心操作包括:
- 1. PUSH(压栈):
- 将数据存储到栈中。
- 栈顶指针(SP)根据栈的增长方向调整。
- 向下增长:SP减小。
- 向上增长:SP增大。
伪代码(向下增长):
PUSH R1 ; 将寄存器R1的值压入栈
SP = SP - 4 ; 栈指针向下移动(假设每次操作4字节)
[SP] = R1 ; 将R1的值存入栈顶
- 2. POP(弹栈):
- 从栈中取出数据。
- 栈顶指针(SP)恢复到上一个值。
- 向下增长:SP增大。
- 向上增长:SP减小。
伪代码(向下增长):
POP R1 ; 从栈顶弹出一个值到寄存器R1
R1 = [SP] ; 将栈顶的值存入R1
SP = SP + 4 ; 栈指针向上移动
函数调用中的堆栈操作
堆栈是一段连续的存储器空间。遵循后入先出、先入后出,后来居上Last in First out 的原则。它只能从顶部(由高地址往低地址逐个往里面放入数据),如果超出存储内存空间,则会出现堆栈溢出的情况(此处暂不进一步讨论)。【主要动作:推入push,取出pull】。
在函数调用中,堆栈寄存器通常参与以下操作:
- 保存返回地址:调用函数时,当前程序计数器(PC)的值会被压入栈中。
- 保存局部变量:函数执行时,局部变量和中间结果会存储到栈中。
- 恢复上下文:函数返回时,从栈中恢复返回地址和局部变量。
示例代码(下面配合图文讲解更加直观,简洁易懂):
void functionA() {
int x = 10; // 局部变量
functionB(); // 调用函数B
}
void functionB() {
int y = 20; // 局部变量
}
对应伪汇编:
; functionA的开始
SUB SP, SP, 8 ; 为局部变量x分配空间
MOV [SP], 10 ; 将x的值存入栈中
BL functionB ; 调用functionB,保存返回地址到栈
ADD SP, SP, 8 ; 回收局部变量x的空间
; functionB的开始
SUB SP, SP, 8 ; 为局部变量y分配空间
MOV [SP], 20 ; 将y的值存入栈中
ADD SP, SP, 8 ; 回收局部变量y的空间
RET ; 从栈中弹出返回地址
堆栈的作用:
- 堆栈可以来完成参数传递和返回值的传递(函数调用);
- 堆栈可以用来保存局部变量、寄存器的值;
- 典型用处是中断,用于保存案发现场。