如何使用细分来扩展内存?

时间:2017-10-20 05:23:04

标签: assembly memory x86

我试图理解记忆模型,特别是分段和平坦。然而,大多数参考资料过于技术化,似乎可以使用分割来扩展"记忆,但这是什么意思,它是如何完成的? 此外,在使用分段存储器模型时,为什么一次只能使用地址64KB?

2 个答案:

答案 0 :(得分:2)

  

为什么一次只能使用地址64KB?

因为这可以通过16位偏移到段中来解决多少问题。

实模式下的x86分割仅为linear address = segreg<<4 + offset,其中偏移是常规寻址模式,如[bx + di]

如果禁用分页,则线性地址是物理地址,否则它是由CR3指向的页表转换的虚拟地址。

使用32位寻址模式,您可以相对于段访问4GiB。保护模式使用段作为描述符表的索引,而不是直接作为基值,因此您可以使用大段和大块。 (但至少启用了分页,it's all mapped to the same 32-bit linear address space,因此您无法使用分段使32位操作系统向用户空间进程提供超过4GiB的内存,即使具有不同基数的分段也是如此4GiB限制。)

在段寄存器中使用不同的值允许访问更多的内存,而不是一次只能访问,这使得像C这样的语言非常不方便,其中指针&#34;只是一个指针。你必须介绍&#34;远指针&#34; vs.&#34;靠近指针&#34;。

答案 1 :(得分:2)

64 KiB限制并非固有的使用段,而是x86段诞生并且使用最多的上下文,即16位x86处理器,其中地址为16位宽,因此只能索引64 KiB存储器中。 1

段本质上是解决此限制的一种技巧:每个段(大致)包含一个基址,您可以隐式或明确地引用16位地址。这使你可以同时拥有5 64 KiB&#34;观看&#34;在较大的物理地址空间内(以后,虚拟地址空间);通过在相关段寄存器中加载不同的地址,也可以移动这些视图。

这个技巧特别有效,因为不同的指令默认使用不同的指令 - 通常主要处理隔离数据 - 默认情况下指的是不同的段。

从CS(代码段)读取代码,因此16位指令指针和近call引用它。堆栈指令(pushpop以及callret等内容)在堆栈段上运行; SP和BP通常相对于SS(堆栈段)进行解释。

除非您使用SP或BP作为基本寄存器,否则可以在DS(数据段)上默认使用可在内存操作数上操作的所有其他指令,当它隐含您正在操作时在堆栈上,假设SS;仍然,您可以添加允许在另一个段上操作的前缀。

最后,字符串指令在DS上读取(可覆盖)和写入时ES。这允许在数据空间(或另一个段)与远远超过64 KiB的另一个位置之间轻松有效地复制数据。典型的安排是将ES指向视频内存并使用字符串指令在屏幕上复制图像数据。

因此,一般来说,即使不通过改变段来玩技巧,你也可以拥有不同的64 KiB堆栈,代码,数据和一个&#34;附件&#34;通过ES地址空间(自80386以来有三个地方,其中添加了FS和GS),因此您可以同时处理高达 2 256 KiB(384 KiB)的内存,仍保持(主要)只有16位指针 - 尽管你必须记住他们提到的段。

现在,一旦你需要实际处理更多内存,就会变得更加困难,从而在程序运行时更改段寄存器。

代码很简单 - 如果你需要调用一个位于当前CS之外的区域中的函数,你需要执行一个&#34;远程调用&#34;传递一个32位远指针;只要远程呼叫和远端返回匹配,没有人试图近距离调用代码,实际上远一切顺利。

数据更加细腻;进入远指针,近指针,各种奇怪的DOS内存模型和东西变得非常快 - 当用更高级别的语言(如C语言)编程时,你不得不担心这种超低级别的细节。我没有真正掌握有关远程指针的内存模型编写软件的第一手经验,所以我不能分享我自己的经验,但每个谈论它的人都非常同意它根本不是很好,我们所有人都很高兴搬到一个指针只是一个指针在一个扁平地址空间内的世界。

注释

  1. 即使在32位平面寻址模式下,您仍然使用段寄存器,但它们都设置为零,因此所有段都相同,并且在您使用的地址和访问的实际内存之间不执行转换。 FS / GS是一个例外,因为它们通常被现代操作系统用来引用包含线程上下文数据的内存块。
  2. 请记住,细分可能会重叠。顺便说一句,这导致了高级语言的进一步复杂化,因为远端指针的数字比较没有给出关于实际指向地址的相等性的信息。