【发布时间】:2019-11-23 15:55:53
【问题描述】:
我试图了解堆栈在推入和拉出东西时是如何工作的,如果问题听起来很简单,对不起。
我想从超级基本的东西开始,比如 8 位内存(我知道这会过于简单,但让我们从简单的开始)
我设计堆栈的方式如下:
SP 最初会指向内存中的最高位置:0xFF
0xFF: <- SP
当推送命令发出时,我会将val保存在SP指向的位置,然后减小SP。
0xFE: <- SP
0xFF: val
弹出命令会先增加 SP,然后将 SP 指向的值移动到寄存器中。
基本上我的 SP 指向堆栈中的第一个可用位置。
然而,这似乎不是它在实际系统中的实现方式。
查看组装手册中的推送指令:
Decrements the stack pointer and then stores the source operand on the top of the stack.
所以基本上 SP 指向的是最新存储的值。
我的问题是:通过首先减少堆栈指针,堆栈的顶部不是不可用吗?如果我们在保存数据之前先减少指针,我们如何将数据存储到堆栈的第一个位置?
有理由这样设计堆栈指针吗?
【问题讨论】:
-
这两种类型都有实现,一些架构同时支持这两种类型。 “第一位置”的概念取决于堆栈类型。您不能写入初始堆栈指针的地址,但您不会将其称为“第一个位置”。或者换一种说法:给定一个预期的第一个位置,将堆栈指针设置为直接指向它之后,然后它就会工作。
-
主要基于意见/过于宽泛。也就是说,考虑存储然后减量的非 x86 方法:第一个有用的数据元素将位于距 SP 的偏移量
1或更大处。执行先减后存储,有效堆栈值的索引从0开始,就像对任何类型的基于数组/指针的存储的常规访问一样。恕我直言,这是一个有用的一致性。这也意味着您没有“浪费”偏移量,即0的偏移量实际上是有意义的,而不是从没用过。 -
先修改SP是一种保护。如果堆栈中的数据副本和 SP 更新之间出现中断,会发生什么情况?如果您先更改SP,则不会出现问题。
-
@AlainMerigot:这对于没有
push指令的 ISA 来说是有意义的,您必须手动减少堆栈。但是,如果您确实有推送指令,则存储和减量将是原子 WRT。中断。 (中断在逻辑上总是发生在指令边界;部分进度被丢弃或中断等待指令完成。)我从未听说过像push这样的简单指令在完成一半指令时可中断的ISA。因此,满堆栈与空堆栈主要是一种任意设计选择,但让 SP 指向数据可能很有用。 -
当然,指令永远不会被打断。但是即使在带有
push指令的机器上,也可以使用一对 sp--/store 指令来压栈,这需要通过首先递减 sp 来防止中断。而且,虽然技术上没有严格要求,但使用推送或一对指令具有完全相同的机制是有意义的。
标签: assembly stack cpu-architecture callstack instruction-set