我知道在我个人熟悉的架构(x86,6502等)中,堆栈通常向下增长(即,推入堆栈的每个项目都会导致递减的SP,而不是递增的SP)。
我想知道这个的历史原因。我知道在一个统一的地址空间中,在数据段的另一端(例如)开始堆栈很方便,所以如果双方在中间碰撞,那么只会出现问题。但是为什么堆栈传统上是最重要的?特别是考虑到这与“概念”模型的对立方式如何?
(请注意,在6502架构中,堆栈也向下增长,即使它被限制在一个256字节的页面上,这种方向选择似乎是任意的。)
答案 0 :(得分:43)
至于历史原因,我不能肯定地说(因为我没有设计它们)。关于此事的想法是早期的CPU将其原始程序计数器设置为0,并且自然希望在另一端启动堆栈并向下扩展,因为它们的代码自然会向上增长。 / p>
另外,请注意,所有早期CPU不会在重置时将程序计数器的此设置设置为0。例如,摩托罗拉6809将从地址
0xfffe/f
获取程序计数器,因此您可以开始在任意位置运行,具体取决于该地址提供的内容(通常,但不限于ROM)。 / p>
某些历史系统最先做的事情之一就是从顶部扫描内存,直到找到一个可以读回写入的相同值的位置,这样就可以知道安装的实际RAM(例如,z80与64K地址空间不一定有64K或RAM,事实上64K在我早期就已经大量了。一旦找到顶部实际地址,它将适当地设置堆栈指针,然后可以开始调用子例程。这种扫描通常由CPU在ROM中运行代码来完成,作为启动的一部分。
关于堆栈增长,并非所有堆栈都向下增长,有关详细信息,请参阅this answer。
答案 1 :(得分:20)
我听到的一个很好的解释是,过去的某些机器只能有无符号的偏移量,所以你希望堆栈向下增长,这样你就可以击中当地人,而不必丢失额外的指令来伪造负偏移
答案 2 :(得分:6)
一个可能的原因可能是它简化了对齐。如果在堆栈上放置一个必须放在4字节边界上的局部变量,则可以简单地从堆栈指针中减去对象的大小,然后将两个较低位置零以获得正确对齐的地址。如果堆栈向上增长,确保对齐变得有点棘手。
答案 3 :(得分:4)
IIRC堆栈向下增长,因为堆向上增长。它可能是另一种方式。
答案 4 :(得分:3)
Stanley Mazor(4004和8080架构师)介绍了如何在"Intel Microprocessors: 8008 to 8086"中为8080(最终是8086)选择了堆栈增长方向:
选择将堆栈指针运行为“下坡”(堆栈向低内存方向前进)以简化从用户程序到堆栈的索引编制(正索引编制)并简化从前面板显示堆栈的内容。
答案 5 :(得分:2)
我认为这纯粹是一个设计决定。并非所有这些都向下增长 - 请参阅this SO thread,以便就不同架构的堆栈增长方向进行一些讨论。
答案 6 :(得分:1)
我认为会议始于IBM 704及其臭名昭着的“减量登记册”。现代语音将其称为指令的偏移字段,但关键是它们向下,不 向上。
答案 7 :(得分:1)
POP
使用的寻址模式与通常用于扫描字符串和数组的寻址模式相同从堆栈中弹出一个值的指令需要做两件事:从内存中读取值,并调整堆栈指针。此操作有四种可能的设计选择:
预增栈指针,然后读取值。这意味着堆栈将“向下”增长(朝向较低的内存地址)。
先递减栈指针,然后读取值。这意味着堆栈将“向上”增长(朝向更高的内存地址)。
先读取值,然后后增量堆栈指针。这意味着堆栈将向下增长。
先读取值,然后后递减堆栈指针。这意味着堆栈将向上增长。
在许多计算机语言(尤其是 C)中,字符串和数组作为指向其第一个元素的指针传递给函数。一个非常常见的操作是按顺序读取字符串或数组的元素,从第一个元素开始。这样的操作只需要上述的后增量寻址模式。
此外,读取字符串或数组的元素比写入元素更常见。实际上,有许多标准库函数根本不执行写入操作(例如 strlen()
、strchr()
、strcmp()
)!
因此,如果您的指令集设计中的寻址模式数量有限,那么最有用的寻址模式将是后增量读取。这不仅会导致最有用的字符串和数组操作,还会导致POP
指令向下增长堆栈。
第二个最有用的寻址模式是后递减写入,可用于匹配的 PUSH
指令。
确实,PDP-11 had postincrement and predecrement 寻址模式产生了向下增长的堆栈。甚至 VAX 也没有 preincrement 或 postdecrement。
答案 8 :(得分:0)
我不确定,但是在那些日子里我为VAX / VMS做了一些编程。我似乎记得内存的一部分(堆??)上升,堆栈下降。当两人相遇时,你就失去了记忆。
答案 9 :(得分:0)
再多2c:
除了提到的所有历史原理之外,我很确定没有理由在现代处理器中有效。所有处理器都可以采用有符号的偏移量,自从我们开始处理多个线程以来,最大化堆/堆栈距离是没有用的。
我个人认为这是一个安全设计缺陷。例如,如果x64体系结构的设计者会颠倒堆栈增长方向,那么堆栈缓冲区溢出就会被消除 - 这是一个大问题。
答案 10 :(得分:0)
在最小的嵌入式系统中,堆栈增长下降的一个优点是可以将单个RAM块冗余地映射到页面O和页面1中,从而允许从0x000开始分配零页面变量,并且堆栈从0x1FF开始向下增长,从而在覆盖变量之前最大程度地增加了增长量。
6502的最初设计目标之一是可以与例如6530结合使用,从而形成具有1 KB程序ROM,计时器,I / O和64字节的两芯片微控制器系统。堆栈和页面零变量之间共享的RAM数量。相比之下,当时基于8080或6800的最小嵌入式系统将是四或五个芯片。