微处理器中使用的堆栈指针是啥?
Posted
技术标签:
【中文标题】微处理器中使用的堆栈指针是啥?【英文标题】:What is a stack pointer used for in microprocessors?微处理器中使用的堆栈指针是什么? 【发布时间】:2010-11-30 15:29:46 【问题描述】:我正在准备微处理器考试。如果程序计数器的用途是保存下一条指令的地址,那么堆栈指针有什么用呢?
【问题讨论】:
【参考方案1】:堆栈是一种 LIFO(后进先出 - 压入堆栈的最后一个条目是弹出时返回的第一个条目)数据结构,通常用于保存堆栈帧(堆栈的位)属于当前函数)。
这包括但不限于:
返回地址。 返回值的地方。 传递的参数。 局部变量。您将项目压入堆栈并弹出它们。在微处理器中,堆栈既可以用于用户数据(例如局部变量和传递的参数)也可以用于 CPU 数据(例如调用子程序时的返回地址)。
堆栈的实际实现取决于微处理器架构。它可以在内存中向上或向下增长,并且可以在 push/pop 操作之前或之后移动。
通常会影响堆栈的操作是:
子程序调用和返回。 中断调用并返回。 代码显式推送和弹出条目。 直接操作 SP 寄存器。考虑一下我的(虚构的)汇编语言中的以下程序:
Addr Opcodes Instructions ; Comments
---- -------- -------------- ----------
; 1: pc<-0000, sp<-8000
0000 01 00 07 load r0,7 ; 2: pc<-0003, r0<-7
0003 02 00 push r0 ; 3: pc<-0005, sp<-7ffe, (sp:7ffe)<-0007
0005 03 00 00 call 000b ; 4: pc<-000b, sp<-7ffc, (sp:7ffc)<-0008
0008 04 00 pop r0 ; 7: pc<-000a, r0<-(sp:7ffe[0007]), sp<-8000
000a 05 halt ; 8: pc<-000a
000b 06 01 02 load r1,[sp+2] ; 5: pc<-000e, r1<-(sp+2:7ffe[0007])
000e 07 ret ; 6: pc<-(sp:7ffc[0008]), sp<-7ffe
现在让我们跟随执行,描述上面 cmets 中显示的步骤:
-
这是程序计数器为零且堆栈指针为 8000(所有这些数字都是十六进制)的起始条件。
这只是使用立即值 7 加载寄存器 r0 并移至下一步(我假设您理解默认行为将移至下一步,除非另有说明)。
这会将 r0 压入堆栈,方法是将堆栈指针减 2,然后将寄存器的值存储到该位置。
这会调用一个子程序。 将程序计数器以与上一步中的 r0 类似的方式被压入堆栈,然后程序计数器被设置为其新值。这与用户级推送没有什么不同,只是它更多地是作为系统级的事情。
这会从根据堆栈指针计算的内存位置加载 r1 - 它显示了将参数传递给函数的方法。
return 语句从堆栈指针指向的位置提取值并将其加载到程序计数器中,同时向上调整堆栈指针。这就像系统级弹出(请参阅下一步)。
从堆栈中弹出 r0 涉及从堆栈指针指向的位置提取值,然后向上调整该堆栈指针。
Halt 指令只是将程序计数器留在原处,一种无限循环。
希望从那个描述中,它会变得清晰。底线是:堆栈对于以 LIFO 方式存储状态很有用,这通常是大多数微处理器执行子程序调用的理想方式。
当然,除非您是 SPARC,在这种情况下,您可以为堆栈使用循环缓冲区 :-)
更新: 只是为了阐明在上述示例中推送和弹出值时所采取的步骤(无论是显式还是通过调用/返回),请参见以下示例:
LOAD R0,7
PUSH R0
Adjust sp Store val
sp-> +--------+ +--------+ +--------+
| xxxx | sp->| xxxx | sp->| 0007 |
| | | | | |
| | | | | |
| | | | | |
+--------+ +--------+ +--------+
POP R0
Get value Adjust sp
+--------+ +--------+ sp->+--------+
sp-> | 0007 | sp->| 0007 | | 0007 |
| | | | | |
| | | | | |
| | | | | |
+--------+ +--------+ +--------+
【讨论】:
这个答案是各种赢。 我喜欢 SPARC 和它的注册窗口 :) @DenysS,当你 push 太多东西时会发生堆栈溢出 - 假设堆栈向下增长,这将是一个递减的 SP。发生什么取决于它遇到什么。如果它运行到您的数据中,您的程序将受到怀疑。如果它运行到您的代码中,则可能是灾难性的,因为代码指令被设置为任意值。超过 ffff 的堆栈实际上是堆栈下溢(弹出太多)。在任何情况下,发生的事情几乎都是废话——任何可能发生的事情,所以你想避免它。 这是我见过的最好的答案之一。 @dust,我不这么认为。第 4 行调用 000b,这就是 PC 中的结果。暂停是唯一不更新 PC 的指令,因此它有效地暂停了程序。这就是为什么它将 PC 设置为 000a。让我知道这是否可以清除或我误解了。【参考方案2】:堆栈指针存储最近被压入堆栈的条目的地址。
要将值压入堆栈,堆栈指针会递增以指向下一个物理内存地址,并将新值复制到内存中的该地址。
要从堆栈中弹出一个值,从堆栈指针的地址复制该值,并将堆栈指针递减,指向堆栈中的下一个可用项。
硬件堆栈最典型的用途是存储子程序调用的返回地址。当子程序执行完毕后,返回地址从栈顶弹出并放入程序计数器寄存器中,使处理器在子程序调用之后的下一条指令处恢复执行。
http://en.wikipedia.org/wiki/Stack_%28data_structure%29#Hardware_stacks
【讨论】:
【参考方案3】:你有更多[为考试]做准备;-)
堆栈指针是一个寄存器,它保存堆栈上下一个可用点的地址。
栈是内存中的一块区域,保留用于存储栈,这是一种 LIFO(后进先出)类型的容器,我们在其中存储局部变量和返回地址,从而可以对嵌套的嵌套进行简单的管理典型程序中的函数调用。
有关堆栈管理的基本说明,请参阅Wikipedia article。
【讨论】:
【参考方案4】:对于 8085:堆栈指针是微处理器中一个特殊用途的 16 位寄存器,它保存堆栈顶部的地址。
计算机中的堆栈指针寄存器可供以低于中断处理程序的特权级别执行的程序进行通用使用。此类程序中的一组指令(不包括堆栈操作)将堆栈指针以外的数据(例如操作数等)存储在堆栈指针寄存器中。当在中断上将执行切换到中断处理程序时,当前执行程序的返回地址数据被压入中断处理程序特权级别的堆栈中。因此,将其他数据存储在堆栈指针寄存器中不会导致堆栈损坏。此外,这些指令可以将数据存储在当前堆栈指针之外的堆栈段的临时部分中。
阅读这篇文章了解更多信息。
General purpose use of a stack pointer register
【讨论】:
天哪,人们真的为这些东西申请了专利吗?真是个瓦罐。我应该为 Q*A 网站发布编程问题和答案申请专利。那么你们所有人都必须向我支付版税。【参考方案5】:堆栈是用于保存临时数据的内存区域。 CALL 指令使用堆栈来保存过程的返回地址。 return RET 指令从堆栈中获取该值并返回到该偏移量。当 INT 指令调用中断时也会发生同样的情况。它在堆栈中存储标志寄存器、代码段和偏移量。 IRET 指令用于从中断调用返回。
堆栈是后进先出 (LIFO) 存储器。数据通过 PUSH 指令放入堆栈,并通过 POP 指令删除。堆栈存储器由两个寄存器维护:堆栈指针 (SP) 和堆栈段 (SS) 寄存器。当一个数据字被压入堆栈时,高 8 位字节被放置在位置 SP-1 中,低 8 位字节被放置在位置 SP-2 中。然后 SP 减 2。SP 添加到 (SS x 10H) 寄存器,以形成物理堆栈内存地址。当数据从堆栈中弹出时,会发生相反的顺序。当从堆栈中弹出一个数据字时,在位置 SP-1 中获得高位 8 位字节,在位置 SP-2 中获得低位 8 位字节。然后 SP 增加 2。
【讨论】:
【参考方案6】:堆栈指针保存堆栈顶部的地址。堆栈允许函数将存储在堆栈中的参数相互传递,并创建作用域变量。此上下文中的范围意味着当堆栈帧消失和/或函数返回时,变量从堆栈中弹出。如果没有堆栈,您将需要为所有内容使用显式内存地址。这将使为架构设计高级编程语言变得不可能(或至少非常困难)。 此外,每种 CPU 模式通常都有自己的存储堆栈指针。因此,当发生异常(例如中断)时,异常处理程序例程可以使用自己的堆栈,而不会破坏用户进程。
【讨论】:
【参考方案7】:如果您渴望更深入的了解,我衷心推荐Patterson and Hennessy 作为介绍,Hennessy and Patterson 作为中高级文本。它们很贵,但确实是无与伦比的;我只是希望当我获得硕士学位并进入为他们设计芯片、系统和系统软件部分的工作时,其中一个或两个都可用(但是,唉!那是太久以前的事了;-)。堆栈指针是如此重要(微处理器和任何其他类型的 CPU 之间的区别在这种情况下非常有意义......或者,就此而言,在任何其他情况下,在过去的几十年里......!-)我怀疑除了一些彻底的从头开始的复习会有所帮助!-)
【讨论】:
nonpariel - 一种小的扁平巧克力滴,上面覆盖着白色的糖粒。嗯,巧克力和糖。哦,你的意思是形容词,“不等于”?好吧,这周我学到了。 @pax, pariel != pareil。 I 在 E 之前,除非不是!-) +1 但我在深夜独自一人时对那本书有邪恶的闪回。这本书很棒……我的书架上还有它。是与它相关联的类对我造成了影响。【参考方案8】:在某些 CPU 上,有一组专用的堆栈寄存器。当执行调用指令时,一个寄存器加载程序计数器,同时第二个寄存器加载第一个寄存器的内容,第三个寄存器加载第二个,第四个加载第三个,等等. 当执行返回指令时,程序计数器被第一个堆栈寄存器的内容锁存,同时该寄存器从第二个寄存器被锁存;第二个寄存器是从第三个寄存器加载的,依此类推。请注意,此类硬件堆栈往往相当小(例如,许多较小的 PIC 系列微控制器具有两级堆栈)。
虽然硬件堆栈确实具有一些优势(例如,推送和弹出不会为调用/返回增加任何时间)但具有可以加载两个源的寄存器会增加成本。如果堆栈变得非常大,用可寻址存储器替换推挽寄存器会更便宜。即使为此使用了一个小的专用存储器,拥有 32 个可寻址寄存器和一个具有递增/递减逻辑的 5 位指针寄存器也比拥有 32 个寄存器每个都有两个输入更便宜。如果应用程序可能需要比 CPU 容易容纳的更多堆栈,则可以使用堆栈指针以及逻辑从主 RAM 存储/获取堆栈数据。
【讨论】:
【参考方案9】:栈指针是一个存储栈顶地址的小寄存器。用于指向栈顶地址。
【讨论】:
以上是关于微处理器中使用的堆栈指针是啥?的主要内容,如果未能解决你的问题,请参考以下文章