【问题标题】:What manages RAM?什么管理 RAM?
【发布时间】:2012-02-26 06:58:20
【问题描述】:

当汇编程序在没有操作系统的机器上编译和运行时,如何选择 RAM 中的起始地址以便正确分配使用数据指令声明的变量?

【问题讨论】:

  • 我认为它通常是由 CPU 预定义的。例如,8086 被硬连线以在地址 FFFF0 处开始执行,假定该地址位于 ROM 内。您使用的是什么处理器架构?每个人的情况可能不同。
  • x86。所以你是说第一个运行的程序得到那个地址?如果有,后续的节目呢?
  • 后续节目,管理。另外,它们真的可以称为程序吗?那时,它们实际上只是数据块,被执行以期成为代码。
  • 第一个程序或任何后续程序的汇编代码是否包含任何显式 RAM 地址,例如 0x00000?
  • 操作系统管理内存;我不确定我是否理解你的问题。操作系统从某个预定地址开始

标签: assembly operating-system memory-management ram


【解决方案1】:

每个处理器,有时是处理器系列的子集,都有不同的引导方案。通常是以下类别之一:

1) 不同类型的中断/事件(复位、中断、nmi 等)的地址表,通常称为向量表,该表位于硬编码位置,硬件上电时读取复位向量处的地址,然后然后在复位向量中指定的地址开始执行代码。

2) 指令表,这些指令的地址位于已知位置,例如 ARM,这样,每个向量/事件获得一条指令,因此一条指令必须是加载 pc 或分支 (或者后面的向量不被使用,然后您可以使用更多指令退出向量表。

3) 让我想想其他人

一些公司(处理器架构)使用高地址,例如 0xFFFE 是复位向量,0xFFFC 是中断。 msp430和6502我觉得是这样的。正如您在其他答案中看到的那样,8088/86 和系列使用 0xFFFxxxFFFF0 作为入口点/重置地址。

ARM 和 Atmel AVR 等其他人使用地址 0x0000 作为入口点。

如果你在处理器内存接口非常简单的时候进入你的 Wayback 机器,一些地址位、数据位、读写选通脉冲,可能还有芯片选择或输出使能或其他东西。您将使用外部逻辑,例如 74LSxx 部件来解码一些高地址位,解码全零和全一很容易,因此您要么在高地址有 rom(高地址位全为 1),要么在低地址有 ram地址(高地址位全为零),反之亦然。即使在今天的微控制器和处理器中,这也让生活变得轻松。所以你的 ARM 和 AVR 会倾向于假设地址 0 是 rom 而 ram 是其他地方。你的 x86 和 msp430 和其他人会将 rom 放在顶部,然后在下方或中间的某个地方向下运行。

您必须用汇编语言对该地址进行硬编码吗?不一定,通常你为链接器硬编码一些东西,以便它把向量放在正确的位置,但是有些汇编器仍然支持 .org 类型指令,你在这个指令之后声明代码将被放置在这个地址内存空间。

每个处理器都有某种引导加载程序,甚至您的电脑也是如此。这是一个在知道处理器引导规则的情况下编译的单个程序,该规则是在该处理器上运行的代码的第一位。从那里它可能会有所不同,您只能拥有一个程序,那就是您没有计划在启动后更改程序,有时这只是称为固件。现在有时引导加载程序会出现某种提示,也许您使用串行端口和哑终端键入命令或使用 xmodem 等下载程序(uboot、redboot 等执行此类操作)。通常您希望在没有人为干预的情况下启动并启动和运行,因此可能有机会在终端/串行端口上按下一个键或在启动时按下板上的一个按钮等。否则引导加载程序有一组规则对于接下来要做什么,这些规则对于引导加载程序的作者来说是独一无二的,这只是你可以做任何你想做的事情的软件。例如,您的计算机此引导加载程序统称为 BIOS,您可能可以按 F1 或 F2 或 DEL 或其他键或键序列并中断正常的引导过程以执行其他操作(更改 BIOS 设置等)。否则,BIOS 有一组规则(有时/经常可以修改),用于查找下一个要加载和运行的内容。通常试图找到一些媒体,如硬盘、U 盘、软盘或包含引导扇区的 cd/dvd rom。该引导扇区只是更多代码,BIOS强制执行有关该代码必须看起来像什么或必须为什么地址等进行编译的规则,它加载该代码然后成为另一种形式的引导加载程序,该代码可以执行任何操作,启动linux,启动windows,启动dos,问你一些关于你想启动什么的问题,运行内存测试程序等等。

你可能会加载另一个程序,一个操作系统,一个有自己遵循的规则的程序,如果它选择允许加载其他程序,这些程序必须遵守规则。有时当你有一个 mmu 时,你可以让程序认为它在一个地址上运行,而实际上它在另一个地址上运行,但这使得你可以强制所有程序假设它们从特定地址运行,然后编译它们对于那个地址(你为 windows/linux 等编写的大多数程序都是这样的)。但是如果你没有 mmu,那么可能会有不同的规则,比如整个程序必须是位置无关的代码(任何地方都没有硬编码地址),我听说 uclinux 就是这样的。

微控制器是学习这类东西的好地方,因为您可能已经有一个引导加载程序(arduinos 就是这样,lpc 和 st 芯片通常有一个引导加载程序、串行或 USB 或两者)。有时,就像 avr 一样,引导加载程序位于您可以修改的闪存中,将引导加载程序闪存和应用程序闪存分开,有时引导加载程序位于闪存中,但只有芯片制造商知道如何修改/更改它,因此您永远只能使用他们的引导加载程序。在任何一种情况下,您仍然可以制作自己的引导加载程序,在此之上您可以创建一种加载其他程序的方法,这些程序退出回引导加载程序以便其他程序可以运行的规则,等等。我有一些说明设置模拟器(在调试的各个阶段,thumbulator 有一些用处将其清除,就像我从其他人那里分叉的 pcemu 一样),您可以包裹一个哑终端并创建自己的环境而不必实际买任何东西(可以用 qemu 和 gdb 做到这一点,但不是那么容易)。

如果您特别研究这些微控制器,但所有处理器都有问题,通常启动代码(包括向量)在 rom 中,一旦您运行,虽然您并不总是希望这些向量是硬编码的向量rom,您要么必须制定某种方案,使 rom 代码以某种方式分支到 ram 中的某些东西,要么有一个硬件方案,在启动后您可以在某处翻转开关(向寄存器中某处的某个字节写入位)和更改在该地址解码的内存,允许您切换 ram 并用软的东西替换向量表。其中一些微控制器为此有一些过于复杂的方案。在这里,您必须再次查看该处理器和/或芯片的文档以了解其工作原理。有时是板上芯片之外的东西导致地址空间发生变化,所以你必须仔细阅读。

因为这个级别的大多数程序员已经知道这些东西,所以文档通常不会详细说明。他们可能只是说明向量表从这里开始,这里是项目,或者它可能会遍历表中指定地址的每个项目,但可能永远不会说明该表的内容是内存中的地址还是要执行的指令。 ..你必须“只知道”。而你的学习方式是通过询问他人,以查看他人代码的形式。

变量和数据由您和编译器管理。例如,您使用的语言和您使用的编译器将创建 .text、.data、.bss 段,并且通过编译器或链接器,您必须为工具提供特定地址,以便为该二进制文件放置这些内容。在这个深度嵌入的案例中,这意味着要查看系统文档(芯片加电路板)以找出每个部件需要在哪里才能正常启动和运行。而且您还必须以某种方式将这些信息放在那里。例如,如果您编写的代码创建了一个 .data (我通常不这样做)段,并且您正在从闪存启动,您必须有一些方案让编译器进行编译,以便 .data 段位于 ram 中,但是当您启动它时不在 ram 中,您必须在 rom 中的某处拥有 .data 段的副本,然后您必须在进入代码主体之前将其复制到 ram。同样,您必须设置堆栈指针,并在需要时修改向量表等。如果使用 dram,您必须使用可能必须在没有内存的情况下运行的代码初始化内存,才能启动 dram,然后复制 .data,零 .bss,设置堆栈指针然后分支到 main()。

【讨论】:

  • 类似.org 0x7c00 的指令(用于传统 x86 PC BIOS MBR 引导扇区)不会导致在该地址加载代码。相反,它告诉汇编器它应该期望代码/数据位于哪个地址。 x86(在 x86-64 之前)没有任何 PC 相对寻址模式,因此像 mov dx, OFFSET msg 这样的指令需要将数据的 绝对 地址嵌入到立即数中。为了正确地做到这一点,汇编器需要知道正确的原点(基地址)来假设位置相关的代码。与位置无关的代码是不必要的,例如使用call/pop
  • @PeterCordes 是的,帮助链接器,您所做的任何事情都不能“导致”代码最终出现在有假设和信任的地方,但这些很容易分崩离析......
  • 有趣的事实:当用许多 x86 汇编器汇编一个平面二进制文件时,没有链接器。例如nasm 默认直接生成平面二进制文件,并且只为带有nasm -felf 或类似的链接器生成.o。所以org 0x7c00(NASM 或 MASM 指令,而不是 GAS 的.org)纯粹是在汇编时,没有链接。不过,GAS 是不同的。您不能直接制作平面二进制文件,需要ld。您甚至可以提供链接描述文件来控制布局。 How to generate plain binaries like nasm -f bin with the GNU GAS assembler?
  • 好吧,当我说链接器时,链接作业的一部分将项目放置在内存空间中,然后将它们放置在输出二进制文件中,无论是组装的图像块还是实际对象。如果有多个 .org 项目,那么您以某种方式将它们链接到二进制文件中。我在考虑 masm 和 tasm 和 basm 以及其他早于这些的东西,其中 nasm 是我们目前维护的克隆。尽管我同意一个带有单个 .org 的简单文件可以更轻松地跳过正式步骤并瞄准最终的二进制文件。
【解决方案2】:

如前所述,CPU 期望启动程序所在的地址通常是硬连线的。在某些非常特定的 CPU 或类似的情况下,它可能是可编程的,但在 x86 的情况下,它是 FFFF0,或者 - 更准确地说 - FFFFFFF0,因此在 CPU 的最高物理地址下方 16 个字节。主板通常将这些地址映射到 ROM,其中包含(很可能)跳转到 BIOS 代码,从而启动计算机。

当涉及到操作系统本身时,它们会选择加载程序的位置,然后进行实际加载,并将执行转移给它。例如,在 DOS 的情况下,简单的小型应用程序(那些作为 COM 文件分发的应用程序)在地址 100 处加载,然后命令提示符执行跳转到该地址,有效地开始执行在该地址加载的代码。对于采用虚拟内存的更先进的系统,问题当然会更复杂。

【讨论】:

  • 编写将在没有操作系统的机器上运行的程序需要明确定义 RAM 地址,因为操作系统负责为其他程序分配内存。对吗?
  • 是的。如果您在没有操作系统的情况下运行程序,则程序本身实际上就是操作系统。 :) 因此,您必须通过给出显式地址来访问每个内存单元。但是,您必须记住,由于内存映射设备等原因,即使在实模式下,普通应用程序也不总是可以使用所有地址。
  • 实模式是一个非常过时的概念。 Ring 0 可能会更好:en.wikipedia.org/wiki/File:Priv_rings.svg
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
  • 2017-07-18
  • 1970-01-01
  • 2016-12-25
  • 1970-01-01
  • 2011-10-03
  • 2020-12-28
相关资源
最近更新 更多