【问题标题】:What defined the typical memory layout?什么定义了典型的内存布局?
【发布时间】:2021-11-18 20:25:24
【问题描述】:

我们都知道典型的内存布局由stack-heap-bss-data-code等组成,

但是,是谁/什么定义了它?

  • CPU:英特尔还是 arm?
  • 工具链:GCC 还是 LLVM?
  • 操作系统:Win 还是 Linux? 还是其他?

还有其他类型的内存布局吗?

【问题讨论】:

  • 它在所有这些组织之前从种子中生长出来。在集成电路之前,您需要深入研究计算历史......
  • 答案通常是“您正在为其编程的平台的 ABI”,但 ABI 再次受到经过数十年改进的现有实践的影响。
  • 在哪里可以找到规格?
  • 如果是您想了解的历史,那么retrocomputing.stackexchange.com 可能是一个更好的提问地点。
  • 这可能是 retrocomputing.stackexchange.com 上的一个更好的问题,因为这可以追溯到历史上。

标签: assembly operating-system


【解决方案1】:

这是非常广泛的,但答案就是以上所有。例如,在 ARM cortex-m 方面,ARM 定义了用于代码、外围设备等的内存空间。如果你不遵循这些,那么事情就不会按你希望的方式工作。同时,每个处理器都有一个引导方案,通常是一个固定地址,用于获取第一条指令或复位向量。所以系统工程师需要围绕它设计电路板或芯片。由于这需要是非易失性的,因此您通常不会在该地址空间(或外围设备)看到 ram,这意味着它们在其他地方,因此可以选择该产品。

一般的向量表或软件中断或其他可能具有硬编码地址的东西会影响您设计系统的方式。然后,您会遇到可能是也可能不是历史侥幸的事情。 IBM PC,他们是谁、如何以及为什么选择他们选择的地址空间?无关紧要,但它为兼容设备设定了一个标准,并一直延续到今天(有一些演变)。

上面的内容只是让你到达没有 ram 的地方。

根据先前设计的约定,大多数情况下基本上是任意的,当前的设计可能会为 ram 使用某些内存空间。

文本堆堆栈模型有些明显,代码通过地址空间向前运行。堆栈是动态的,让堆栈在内存空间中向上增长是没有意义的,而是从顶部向下增长,尽管随着我们的发展,我们一开始可能没有理解这一点。当然,今天有了 MMU,你就拥有了类似于历史的虚拟空间,但物理空间是你想要的任何东西,散布在 ram 周围的碎片中。当堆栈没有意义时,这会将内存留在中间供程序动态分配。

在 .text、.data、heap 和 stack 的虚拟(或真实)地址空间中,是当今实现(windows、linux 等)中操作系统和编译器的组合。作为一名程序员,如果您完全了解操作系统规则,则可以自定义链接描述文件,使其与该操作系统的目标编译器的默认链接脚本不完全相同。并且没有理由假设 llvm 和 gcc 针对相同的目标和操作系统遵循相同的解决方案。尽管您可能会看到他们根据谁先来使用相同或相似的解决方案,或者如果程序员(包括他们的内部开发人员)在工具之间来回切换,他们的生活会更轻松。

现在这是针对在操作系统上运行的应用程序。当您进入裸机时,包括操作系统本身(可以被视为裸机程序本身),情况就会发生变化。通常,您是程序员来创建满足您需求的内存空间。您将对诸如 mcus 之类的事物制定规则,其中非易失性和易失性空间由芯片供应商确定。但是在那个空间内,您可以选择,仅仅因为您可以在闪存/ROM 之外运行代码,您不必这样做,并且出于性能或其他原因可能会选择不这样做。同样对于向量表,您可以选择使用启动芯片所需的基于闪存的地址空间,但取决于产品(不一定是处理器内核,芯片供应商控制地址空间),您可能有选择,也可能没有。

因此,当您使用裸机时,尤其是当您从闪存启动时,规则就不存在了。 .data 例如需要存在两个地方,一个是非易失性内存,一个是复制到它在 ram 中的位置。 bss 类似的东西。 .text 可以根据您的选择同时存在于闪存和/或内存中,内存部分从闪存复制(或下载)。

由于您没有远程提供足够的信息,答案只能是“取决于”和“以上所有”。

【讨论】:

    【解决方案2】:

    我认为您正在寻找的答案将通过研究第一个在硬件中具有调用堆栈支持的处理器来提供,因为在此之前很常见,处理器没有明确支持堆栈并且通常不会有内存布局中的任何堆栈。

    另一方面,在过去,代码和数据是混合在一起的。数据往往靠近使用它的代码,因此我们会看到代码和数据的交替。在这里,这些数据实际上是全局的,但这些数据将用于局部变量以及全局变量/静态变量。所以,对于这样的处理器,也不会有数据或bss,只有程序文件,在程序的末尾(到内存的末尾)会有一些空白。

    谈到我使用 PDP-8 的经验,它有 4k(12 位)字的存储空间和一个累加器寄存器(当然没有堆栈指针寄存器):

    程序文件将被加载(最多 4k)以初始化内存,并且程序将从 PC = 0 开始(通常是跳转到“main”)。地址空间为 4k,但内存也始终为 4k,即始终完全填充。

    程序加载后的空闲内存间隙(直到内存末尾)将用作 I/O 缓冲区、可变大小的数组,有时还以类似堆的方式使用,尽管使用特定于应用程序的简化分配而不是比一些通用的malloc/free 库。

    当然,在技术上可以支持递归,但由于堆栈指针必须位于内存中,因此与标准参数传递方法(“内联”)、局部变量分配(全局变量!),因此如果需要递归,则更有可能使用临时和自定义的特定于函数的堆栈(不会被其他非递归代码使用/共享)转换为非递归版本,并且会堆叠东西但是不一定是现代风格的堆栈框架。

    自 PDP-8 以来的变化是:

    • 添加了堆栈 — 一个专用的(或与 MIPS 和其他类似的专用)堆栈指针寄存器和一些 addressing modes 以提高堆栈处理效率(例如推入/弹出、带有偏移量的堆栈指针),因此局部变量和参数通过这种方式而不是旧方式。

    • 代码和(可变)数据已分离,因此可以将代码与数据分开保护(例如,不可写或仅对代码执行,而对数据可写)。

    进一步阅读:

    【讨论】:

      猜你喜欢
      • 2012-04-04
      • 1970-01-01
      • 1970-01-01
      • 2016-09-21
      • 1970-01-01
      • 1970-01-01
      • 2019-07-24
      • 2019-06-09
      • 2017-02-17
      相关资源
      最近更新 更多