处理器的字节顺序与堆栈增长的方向之间是否存在关系?
例如,x86 体系结构是小端,堆栈向下增长(即,它从最高地址开始,每次push
操作都向低地址增长)。类似地,在SPARC 体系结构中,即大端,堆栈从最低地址开始,向上向更高地址增长。
这种关系模式几乎在所有架构中都可以看到。我相信这个未说的约定一定是有原因的。这可以从计算机体系结构或操作系统的角度来解释吗?这是为了对处理器内部的微码进行一些优化吗?这是否以某种方式帮助内核?或者可能是其他原因?
提前致谢!
处理器的字节顺序与堆栈增长的方向之间是否存在关系?
例如,x86 体系结构是小端,堆栈向下增长(即,它从最高地址开始,每次push
操作都向低地址增长)。类似地,在SPARC 体系结构中,即大端,堆栈从最低地址开始,向上向更高地址增长。
这种关系模式几乎在所有架构中都可以看到。我相信这个未说的约定一定是有原因的。这可以从计算机体系结构或操作系统的角度来解释吗?这是为了对处理器内部的微码进行一些优化吗?这是否以某种方式帮助内核?或者可能是其他原因?
提前致谢!
堆栈增长方向与整数字节序正交。
在更宽的整数(字)内的字节顺序与堆栈推送是否从堆栈指针中添加或减去之间存在零连接。就推送而言,存储数据是一个单一的操作。
将寄存器宽度整数映射到内存中的字节使用与堆栈指针 inc/dec 逻辑不同的硬件;我假设正常的设计是使用与非推送/弹出存储/加载相同的硬件,并将推送中的存储视为“单词”的任何其他存储。不是任何一种奇怪的一次一个字节的东西,一个一个地递增堆栈指针。
这种关系模式几乎在所有架构中都可以看到。
呃,不是真的。许多现代 RISC ISA(例如 MIPS、PowerPC、ARM)具有可选择的字节序1,这与堆栈增长方向无关。
大多数现代系统中堆栈增长的方向是什么?表明在大多数主流系统上,堆栈增长方向通常按照惯例或要求向下,包括在大端系统上。
根据该问答的答案,SPARC 上的主流操作系统/ABI 选择向下扩展堆栈。向上是 SPARC 上的一个选项,但与其他系统一样,正常的选择是向下。
这可以从计算机体系结构或操作系统的角度来解释吗?
我们可以解释的是,向下是事实上的标准。IDK 为什么 SPARC 费心做出向上的选择。堆栈在可用内存的顶部,静态代码/数据在底部的固定地址,显然没有分页/虚拟内存是自然的。 https://softwareengineering.stackexchange.com/questions/137640/why-does-the-stack-grow-downward
这就是我们到达这里的方式。
在某些 ISA(例如 MIPS)上,堆栈增长的方向完全由软件确定。硬件不会隐式/异步使用堆栈,并且没有推送/弹出指令可以更有效地采用一种或另一种方式。
但正常的选择还是向下的。
其他 ISA 类似于 x86,其中异步中断将内容推送到内核堆栈,从而强制一个方向。或者至少在一种方式上有偏差,通过为一个方向提供有效的推送/弹出(如 ARM Thumb 和 x86 的push
/ pop
)。更不用说 x86 的call
/ret
指令推送/弹出返回地址,而不仅仅是编写链接寄存器。
如果没有方向选择,则在大多数 ISA 中一个固定的方向是向下,但 @chux 评论说 PIC24 具有向上的堆栈增长。
我很确定所有这些都有大端和小端示例,或者至少可以配置为大端或小端的双端系统。
脚注 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
对于堆栈,它采用相反的字节序,因此,当我们从中弹出数据时,它会重新假定正确的字节序。这是另一个示例,但使用了堆栈。
无符号数据 = 0x0A0B0C0D;
&data = 0x0D
&data+1 = 0x0C
&data+2 = 0x0B
&data+3 = 0x0A
当 we 时push data
,数据的小端存储为大端,因为堆栈是 LIFO/FILO,因此弹出保留了端序,因此:
[ebp] == 0x0A
[ebp-1] == 0x0B
[ebp-2] == 0x0C
[ebp-3] == 0x0D