少于 1 分钟阅读 次阅读

概要:讲解 CPU 运行程序时栈(Stack)的三大作用:记录函数调用的返回地址、存储局部变量和参数、以及中断/异常时保存现场(trapframe),并说明栈“后进先出”特性与这些作用的联系。

前面介绍了各类寄存器,其实归根结底,栈(Stack)是 CPU 运行程序时必不可少的一本”临时草稿本”或者说”备忘录”。

寄存器的运行速度非常快,但容量极小(只有寥寥几个通用寄存器)。当程序越来越复杂,寄存器记不下那么多信息时,就必须有一块专门的内存区域来帮忙记事,这就是栈。

具体来说,栈在底层主要承担以下三大职责:

1. 记录”回家的路”(函数调用的基石)

这是栈最核心的功能。假设程序在运行 main 函数,其中调用了 A 函数,而 A 函数里又调用了 B 函数。

  • 当 CPU 从 A 跳到 B 去执行时,它必须记住执行完 B 之后,要回到 A 的哪里继续运行。
  • 如何记录?执行 call B 指令时,CPU 硬件会自动把 A 函数的下一条指令地址(当前的 %eip压入栈中
  • B 函数执行完毕,执行 ret(返回)指令时,CPU 会自动从栈顶弹出一个地址写回 %eip,从而准确无误地返回 A 函数。
  • 为什么用栈? 因为函数调用是嵌套的(A->B->C),返回的顺序正好相反(C->B->A)。栈这种”后进先出(LIFO)”的结构,与函数嵌套的返回规律完全吻合。

2. 当作”临时工作台”(局部变量和参数)

编写 C 语言时,函数内部经常会定义一些局部变量,例如 int x = 10;,或者给函数传参 func(a, b)。这些数据存放在哪里?

  • 分配极快:全部存放在栈上。进入函数时,只要把 %esp(栈顶指针)向下移动一段距离(比如减 16 个字节),就腾出一块内存来存放局部变量。
  • 自动回收:当函数执行完准备退出时,只要把 %esp 向上推回原来的位置,刚才那些局部变量占用的空间就会立刻失效(成为无效数据,等待下一个函数覆盖)。这也是 C 语言中局部变量在函数外部无法使用的底层原因——工作台已经被收回了。

3. 中断/异常时保存现场(trapframe)

这一点在前面介绍内核栈时已经提到。

当系统正在运行普通程序,突然网卡触发了硬件中断,CPU 必须立刻停下手中的工作去处理网卡数据。

  • 为了在处理完成后能完美恢复原来的程序,CPU 必须把当前所有的状态(各个寄存器的值)保存到一个安全的地方。
  • 这个安全的地方就是内核栈。CPU 会在内核栈上依次 push,把寄存器逐个压入(形成 trapframe)。等内核处理完毕,再逐个 pop 出来,系统便恢复原状,继续运行原来的程序。

总结

如果没有栈,程序就只能从头到尾一条线地执行,不能调用函数,不能有局部变量,操作系统也无法处理中断

因此,栈的本质就是:一块由 %ss%esp 共同看管的、专门用来存放”临时控制信息”和”临时数据”的后进先出内存区。 使用完毕后立即释放,效率极高。

留下评论