【问题标题】:Assembly: Stack vs Heap?组装:堆栈与堆?
【发布时间】:2012-10-12 13:49:21
【问题描述】:

在 x86 汇编中,esp 是堆栈指针,而 ebp 是堆指针吗?为什么我们有两种数据结构而不是只有一种?

esi/edi代表什么?

【问题讨论】:

标签: assembly heap-memory stack-memory


【解决方案1】:

一般来说,机器架构上的任何寄存器都不需要专门用于任何目的。实际上,通过指定一个寄存器来支持常用功能,该寄存器成为该/那些功能的特殊用途。

在 x86 上,ESP 指针被 PUSH/CALL 指令和各种中断/陷阱用来存储可恢复状态,例如返回地址。这些在支持编程方面非常有吸引力,因此由编译器和操作系统供应商承诺,ESP 基本上不能用于任何其他目的。因为可以从任意索引中引用内存,所以它可以用于管理“堆栈”和当前函数堆栈帧。

但是,变量(当前堆栈帧的局部变量)相对于 ESP 的索引偏移量会随着程序将值推送和弹出堆栈以及 ESP 的变化而变化。编译器可以跟踪所有这些,因此可以使用正确的索引偏移量。但是如果你是手工编写汇编代码,你实际上不能可靠地做到这一点,而且更容易拥有一个基于堆栈帧的指针;那么变量的索引偏移量可以是相对于堆栈帧指针的常量。英特尔很早就为此目的指定了 EBP,甚至还有特殊的索引模式来支持这一点,以及用于构建 display 的特殊 EBP 相关指令(一组指向词法封闭堆栈帧的指针,以阿尔戈尔风格)。

但您/编译器不必为此目的使用 EBP。在具有少量寄存器(x86)的机器中,拥有另一个空闲寄存器可以帮助生成更小/更紧凑的代码。在较旧的 CPU 上,不使用 EBP 有不同的惩罚:局部变量的 ESP 索引偏移量往往大于堆栈帧基数的索引偏移量,因此使用 EBP 作为空闲寄存器可以通过更大的偏移量为您提供更大的程序用于 ESP。当获取指令很昂贵时,这曾经很重要。由于指令缓存与它们一样好,这几乎不再是问题。微软的 C 编译器(我怀疑是 GCC)有一个不使用帧指针的开关,从而放弃了一些程序大小来获得更快的代码。

堆变量只是那些不在堆栈帧中的变量。您可以使用任何寄存器来访问它们。

某些语言(例如,我们的 PARLANSE 编译器)仅使用堆分配;甚至堆栈帧都是从堆中获取的(并且 ESP 设置为分配的空间块的顶部)。

【讨论】:

    【解决方案2】:

    通常,ESP 指向堆栈的顶部,EBP 指向堆栈帧的底部,因此它们都指向堆栈上的地址,而不是堆上的地址。在大多数情况下,并不完全需要保留指向栈帧底部的指针,因此一些编译器可能会简单地将 EBP 用作另一个通用寄存器。

    ESI 和 EDI 是通用寄存器,但有一个例外:字符串指令(例如 movs、lods)分别将它们视为源地址和目标地址。

    【讨论】:

      【解决方案3】:

      整个内存被组织成 4 个段。代码、数据、堆栈和其他。堆用于多用途存储(如动态分配(malloc 等)),而堆栈专门用于指令指针和在调用另一个过程时(如在递归期间)存储变量。数据段用于全局变量和静态变量。

      对于 ESI/EDI,请在 SO 上参考此问题:Purpose of ESI & EDI registers?

      编辑:也看到这个:heap vs data segment vs stack allocation

      【讨论】:

      • 全局变量在堆上吗?我一直以为只有malloc'd 内存在堆上?
      【解决方案4】:

      虽然有一些特定于寄存器xBPxSIxDI 的事物和行为,但它们并不绑定到任何一个函数。您可以在汇编代码中随意使用它们。

      【讨论】:

      • 你可以回答“随你喜欢”。
      • @mcandre 喜欢问题,喜欢答案。不喜欢这个答案?在问题中添加细节,也做一些功课。
      • 这是最好的答案之一,不知道为什么它被否决了。 OP假设寄存器具有功能并且该假设是错误的,尤其是堆,他不是硬件的功能,也不是像堆栈这样的汇编语言,它由用户直接(汇编)或间接(具有pre - 为操作系统制作构建脚本/分配)。
      猜你喜欢
      • 2014-06-23
      • 2011-05-04
      • 2012-01-18
      • 2018-12-10
      • 2011-10-06
      • 1970-01-01
      • 2023-03-09
      • 2021-02-18
      • 1970-01-01
      相关资源
      最近更新 更多