例如,x86架构是小端模式,堆栈向下增长(即在最高地址开始,并随着每次push操作向低地址增长)。同样,在大端模式的SPARC体系结构中,堆栈从最低地址开始向上增长到更高地址。
几乎所有体系结构都遵循这种关系模式。我相信这种不成文的约定必须有原因。这可以从计算机体系结构或操作系统角度解释吗?这在处理器内部的微代码优化方面有所帮助吗?这对内核是否有所帮助?还是其他原因?
提前致谢!
堆栈增长方向与整数字节序无关。
在更广泛的整数(字)中,字节顺序和堆栈推送是添加还是减去堆栈指针之间没有任何联系。对于推送而言,存储数据是一个单一的操作。
将寄存器宽度的整数映射到内存中的字节使用与堆栈指针 inc/dec 逻辑不同的硬件; 我假设正常的设计将使用与非 push/pop 存储/加载相同的硬件,并将来自 push 的存储视为“字”的任何其他存储,而不是一次递增堆栈指针的奇怪的一个字节。
这种关系模式在几乎所有体系结构中都可以看到。
呃,其实并不是这样。许多现代 RISC ISA(例如 MIPS、PowerPC、ARM)具有可选择的字节序1,这与堆栈增长方向无关。
大多数现代系统中堆栈增长的方向是什么?表明,在大多数主流系统上,堆栈增长方向通常是向下的,这可能是按惯例或要求而定,包括在大端系统上。
根据该问答中的回答,Sparc上的主流操作系统/ABI选择向下增长堆栈。在Sparc上向上增长是一种选择,但像其他系统一样,向下增长是正常选择。
这可以从计算机架构或操作系统角度解释吗?
我们可以解释为什么向下是事实上的标准。不知道为什么SPARC费心使向上成为一个选项。在没有分页/虚拟内存的情况下,位于可用内存顶部的堆栈,静态代码/数据位于底部的固定地址显然是自然的。 https://softwareengineering.stackexchange.com/questions/137640/why-does-the-stack-grow-downward
这就是我们到达这里的方式。
在一些ISA上(如MIPS),堆栈增长方向完全由软件确定。硬件不会隐式/异步使用堆栈,并且没有推送/弹出指令使得一种方式更加高效。但通常的选择仍然是向下的。我非常确定这些都有大端和小端的例子,或者至少有可以配置为大端或小端的双端系统。
注1:一些特定的ARM或MIPS CPU硬连了它们的字节序,无法在运行时选择,因为对于嵌入式系统来说这基本上是一个无用的功能,也是硅片浪费。不确定现代POWER硬件是否也是如此;Godbolt编译器浏览器(https://godbolt.org/)有PowerPC64和PowerPC64le编译器,但这并不能告诉我们它们是否仍然相关。
字节序是数据的字节顺序。
考虑十六进制值0x0A0B0C0D
,可分为4个字节:0x0A,0x0B,0x0C,0x0D
。
就内存而言:
假设我们有一个内存地址o
memory[o] = 0x0A
memory[o+1] = 0x0B
memory[o+2] = 0x0C
memory[o+3] = 0x0D
memory[o] = 0x0D
memory[o+1] = 0x0C
memory[o+2] = 0x0B
memory[o+3] = 0x0A
使用栈时,采用相反的字节序,以便在从栈中弹出数据时,重新采用正确的字节序。以下是另一个示例,但使用栈。
unsigned data = 0x0A0B0C0D;
&data = 0x0D
&data+1 = 0x0C
&data+2 = 0x0B
&data+3 = 0x0A
当我们push data
时,数据的小端字节序被存储为大端字节序,因为栈是LIFO/FILO,因此弹出保留了字节序,因此:
[ebp] == 0x0A
[ebp-1] == 0x0B
[ebp-2] == 0x0C
[ebp-3] == 0x0D
[ebp-0..3]
)是不对齐的,在地址[ebp-3]
处存储了32位。如果我们假设传统的堆栈帧,那么这将重叠一个字节到保存的EBP值中。实际上,如果在堆栈帧设置的一部分之后立即执行push
,则数据将保留在[ebp-4]
=[esp]
处。 - Peter Cordes[esp]
加载一个字节,你就会得到你刚刚压入的整数的最高有效位(MSB)。我完全看不出你的观点。 - Peter Cordes