use*_*986 6 embedded callstack cpu-architecture endianness
处理器的字节序与堆栈增长方向之间是否存在关系?
例如,x86 架构是小端的,堆栈向下增长(即它从最高地址开始,并随着每个push操作向低地址增长)。类似地,在大端的SPARC 体系结构中,堆栈从最低地址开始并向上增长到更高的地址。
这种关系模式几乎可以在所有架构中看到。我相信这个未说明的约定一定是有原因的。这可以从计算机架构或操作系统的角度解释吗?这是为了对处理器内部的微码进行一些优化吗?这是否以某种方式帮助内核?或者可能是其他原因?
提前致谢!
堆栈增长方向与整数字节顺序正交。
更宽的整数(字)内的字节顺序与堆栈推送是否从堆栈指针中添加或减去之间存在零联系。就推送而言,存储数据是单个操作。
将寄存器宽度整数映射到内存中的字节使用与堆栈指针递增/递减逻辑不同的硬件;我假设正常的设计是使用非推送/弹出存储/加载所经历的相同硬件,并且将推送存储与“单词”的任何其他存储一样对待。不是任何一种奇怪的一次一个字节的事情,它会逐一递增堆栈指针。
这种关系模式几乎出现在所有架构中。
呃呃,其实不是。许多现代 RISC ISA(例如 MIPS、PowerPC、ARM)具有可选择的字节顺序1,并且与堆栈增长方向无关。
大多数现代系统中堆栈增长的方向是什么?表明在大多数主流系统上,堆栈增长方向通常按照惯例或要求向下,包括在大端系统上。
根据该问答的答案,SPARC 上的主流操作系统/ABI 选择向下扩展堆栈。SPARC 上有向上选项,但像其他系统一样,正常选择是向下。
这可以从计算机体系结构或操作系统的角度来解释吗?
我们可以解释的是,向下是事实上的标准。我不知道为什么 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 编译器,但这并不能告诉我们它们是否仍然相关。
Ner*_*gal -1
Endianness 是一段数据的字节顺序。
考虑其十六进制值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
当我们 时push data,数据的小字节序存储为大字节序,因为堆栈是 LIFO/FILO,因此弹出保留字节序,因此:
[ebp] == 0x0A
[ebp-1] == 0x0B
[ebp-2] == 0x0C
[ebp-3] == 0x0D
| 归档时间: |
|
| 查看次数: |
2250 次 |
| 最近记录: |