汇编分段模型32位内存限制

5
如果一个32位操作系统采用分段式内存模型,它们仍然会有4GB的限制吗?
我正在阅读《英特尔奔腾处理器系列开发人员手册》,它指出,在分段式内存模型下,可以映射多达64TB的内存。
“在内存组织的分段模型中,逻辑地址空间由多达16,383个最大为4GB的段组成,或者总共最大为2^46字节(64TB)。处理器通过第11章描述的地址转换机制将这64TB逻辑地址空间映射到物理地址空间。应用程序员可以忽略此映射的详细信息。分段模型的优点在于可以单独检查每个地址空间内的偏移量,并且可以单独控制对每个段的访问。”

alt text

这不是一个复杂的问题。我只是想确保我正确理解了文本。如果Windows或其他任何操作系统采用分段模型而不是平面模型,内存限制是否为64TB?

更新:

alt text

英译中:英特尔的3-2 3a系统文档。

alt text

http://pdos.csail.mit.edu/6.828/2005/readings/i386/c05.htm


段寄存器不应被视为传统实模式的意义。段寄存器作为全局描述符表的选择器。
在保护模式下,您使用形式为A:B的逻辑地址来寻址内存。与实模式类似,A是段部分,B是该段内的偏移量。保护模式下的寄存器限制为32位。32位可以表示0到4Gb之间的任何整数。因为B可以是0到4Gb之间的任何值,所以我们的段现在具有最大4Gb的大小(与实模式的推理相同)。现在的区别在于,在保护模式下,A不是段的绝对值。在保护模式下,A是一个选择器。选择器表示全局描述符表(GDT)中的偏移量。GDT包含描述符列表。每个描述符都包含描述段特性的信息。
段选择器提供了无法通过分页实现的额外安全性。
这两种方法[分段和分页]都有各自的优点,但是分页要好得多。尽管分段仍然可用,但作为内存保护和虚拟内存的一种方法,它正在迅速变得过时。事实上,x86-64体系结构需要一个平面内存模型(一个基地址为0,限制为0xFFFFFFFF的段)才能正常运行某些指令。 然而,分段是完全内置在x86架构中的。这是不可能绕开的。因此,在这里,我们将向您展示如何设置自己的全局描述符表——一个段描述符列表。 正如之前提到的,我们将尝试建立一个平面内存模型。段的窗口应该从0x00000000开始,并延伸到0xFFFFFFFF(内存的末尾)。然而,分段可以做到分页所不能做的一件事,那就是设置环级别。

-http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html

一个 GDT 例如列出了各个用户的访问级别和内存访问区域: 样例 GDT 表格
GDT[0] = {.base=0, .limit=0, .type=0};             
// Selector 0x00 cannot be used
GDT[1] = {.base=0, .limit=0xffffffff, .type=0x9A}; 
// Selector 0x08 will be our code
GDT[2] = {.base=0, .limit=0xffffffff, .type=0x92}; 
// Selector 0x10 will be our data
GDT[3] = {.base=&myTss, .limit=sizeof(myTss), .type=0x89}; 
// You can use LTR(0x18)

http://wiki.osdev.org/GDT_Tutorial#What_should_i_put_in_my_GDT.3F

分页部分是映射到物理内存的部分。(PAE) 可以提供额外的内存,最多可达64GB。

简而言之,答案是否定的,您不能拥有超过4GB的逻辑内存。我认为在《英特尔奔腾处理器家族开发人员手册》中声称可以达到64TB是印刷错误。


这个链接对我有所帮助,它是关于保护模式的文章。 - user295190
是的,这个“打印错误”让我花了4个小时搜索所有由此引起的无意义问题。 - Jay Patel
5个回答

2

编辑:我的回答假设您所说的“4GB限制”是指线性(虚拟)地址空间的最大大小,而不是物理地址空间的大小。如下面的评论所解释的那样,后者实际上根本没有限制为4GB-即使使用平坦内存模型。


重复引用,加强语气:

逻辑地址空间由多达16,383个每个最多4 GB的段组成

现在,引用自“Intel® 64和IA-32体系结构软件开发人员手册卷1:基本架构”(PDF可在此处下载):

在内部,为系统定义的所有段都映射到处理器的线性地址空间中。

正是这个线性地址空间(在32位处理器上)受到4GB的限制。因此,分段内存模型仍然受到此限制。


不...完全正确。虽然已经足够接近,但是 Pentium 系列的后续处理器扩展(例如 PAE 和 PSE-36 等)实际上提供了一个比 4GB 大得多的线性地址空间。事实上,大约是 16 倍大。如果操作系统使用恰当的段,单个进程就可以访问所有这些空间(自然减去内核为自身保留的部分)。 - JUST MY correct OPINION
3
PAE和PSE-36可以扩展4GB以上的物理地址空间,但是线性(虚拟)地址仍然受限于32位。 - user200783
不是为了反对而是为了澄清,从Windows的角度来看:如果使用适当的选项启动,则Microsoft Windows会实现PAE,但当前的32位桌面版本即使在PAE模式下也会强制执行4GB以内的物理地址空间。根据Geoff Chappell的说法,微软将32位版Windows限制在4GB内是因为许可限制,而微软技术院士Mark Russinovich则表示,在遇到4GB以上的物理地址时,发现一些驱动程序不稳定。——维基关于PAE - user295190
由于多任务计算系统通常定义一个线性地址空间,它比在物理内存中一次性包含所有内容更为经济实惠,因此需要某种“虚拟化”线性地址空间的方法。这种线性地址空间的虚拟化是通过处理器的分页机制来处理的。- Intel 3-2 Vol3A Page 94。这句话似乎与您说的32位进程的线性地址空间仅限于4GB相反。听起来线性地址空间比可行的内存要大。 - user295190
1
神话:PAE可以将虚拟地址空间扩展到4GB以上:http://blogs.msdn.com/b/oldnewthing/archive/2004/08/18/216492.aspx 我发誓英特尔的文档可能会误导人。 - user295190

2
你还记得旧时代吗?在x86上使用DOS实模式和64kb段?FAR指针?HMA?XMS?随着内存的增长,他们找到了一些方法来使用比处理器正常寻址能力更多的内存。但这很丑陋。
当然,他们可以为32位使用分段,但是为什么呢?没有必要。当32位处理器出现时,4GB的限制已经足够了,因此决定采用平面模型。
此外,32位操作系统可以使用超过4GB的内存,进程只受4GB地址空间(甚至在Windows上只有2或3GB)的限制。

这个过程只是因为它是一个平面模型而受到限制吗?如果它是分段模型会怎样呢? - user295190
@Shiftbit 不要混淆可直接寻址的地址范围和分段访问。你可以使用WME来访问更多的内存,但是你仍然不能直接做char* c = malloc(5*GB)并从中读取任何值而不使用任何包装器。 - ruslik
@Shiftbit 这里有一个更好的例子。你想为一个拥有1M个64kb段的处理器编程吗?它将拥有64Gb的地址空间。 - ruslik

2
该声明指的是64TB的逻辑地址空间。提及物理内存限制并不相关,因为通过启用内存分页,可以绕过物理限制。
然而,这仍然是一个有些误导的说法,因为段选择器的索引字段是16位,减去1位的表指示器和2位的请求保护级别,留下了总共8192个(13位)段选择器。使用8192个4GB段,只能在GDT(Global Descriptor Table)或LDT(Local Descriptor Table)中访问32TB的逻辑内存。要访问64TB的逻辑内存,必须充分利用GDT和LDT的16384个唯一段。
无论如何,第一个问题是“是否存在4GB限制”,答案是“否”。在启用分段和分页的32位系统上,例如,可以将512MB分配给代码段(CS),1GB分配给堆栈段(SS)和4GB分配给数据段(DS)。
对于第二个问题,即如果操作系统使用分段内存模型会受到64TB的限制吗,答案就不那么直接了。操作系统的工作是提供内存管理器。显然,物理内存的限制是32GB。由于Linux使用分页,32位Linux可以为每个应用程序提供4GB的平面地址空间(忽略内核/用户拆分的细节)。每个进程都认为它有4GB的物理地址空间。
简而言之,您可能将分段的限制与分页的限制混淆了。分页使系统或应用程序能够使用物理上不可用的更多RAM。分段使进程能够映射多个32位逻辑可寻址段。需要注意的是,即使在平面模式下也使用分段,但所有段寄存器都映射到同一个基地址。

分段在分页之前被“解决”。启用分页后,段基址+偏移量生成32位的“线性”虚拟地址。即段基址是虚拟的,并不会扩展您可以在任何给定时间映射的内存量。要访问超过4GiB的物理RAM,您需要具有单独虚拟地址空间的多个“线程”/进程,或者需要将不同部分的物理RAM重新映射到您的32位虚拟地址空间中。当然,也可以使用x86-64长模式。 - Peter Cordes
我不确定禁用分页后会发生什么。没有分页,你就不能使用PAE,但如果将段限制设置为4G / 基址=3G(或略低于4G),我不知道会发生什么。可能你可以访问一些物理内存的部分,超出了低4G,使用从高基地址开始的32位偏移量。或者,如果线性地址仍然是32位,则可能不行。 - Peter Cordes

0
据我所知,答案是“不一定”,因为操作系统还有其他限制。他们可能想要将内存最大大小保持在理论限制以下,因为这可以使一些内部内存结构更小且性能更好。但我真的不知道...我不是马克·拉西诺维奇。
看看PAE。我认为这就是你所谈论的内容,但由于我已经升级到64位指针,我已决定用肯塔基州直接波旁威士忌来清空与窗口内存模型相关的脑细胞。

我特别喜欢维基百科上的这句话:“x86处理器硬件架构通过添加额外的地址线来选择额外的内存,因此物理地址大小从32位增加到36位。这在理论上将最大物理内存大小从4GB增加到64GB。虚拟地址的32位大小未更改,因此常规应用程序软件继续使用具有32位地址的指令,并且(在平面内存模型中)仅限于4GB的虚拟地址空间。” - user295190

-1
Intel的分段模型仅限于16,384个段。这个数字太小了,无法真正方便地处理事情。如果系统能够快速在两亿或四十亿个段之间切换,那将会更好。我希望看到的是一种设计,可以有效地将每个已分配对象放入不同的段中,在不需要额外开销的情况下对每个单独分配的对象进行范围检查,通过最小化影响正在运行代码的对象重定位(假设CPU可以注意到当前选择的段已失效),在缓存中只需要占用64位指针一半大小的对象引用即可。

听起来你想要一个32位指针成为段选择器。(并且寻址模式必须定义哪个组件是段,哪些是偏移量?) 如果CPU硬件必须从段描述符表中查找段基址/限制,则您刚刚重新发明了虚拟内存页表,但可能没有固定的页面大小。因此,这些表将会很大。你肯定需要一些类似TLB的结构来缓存最近使用的段基址/限制,因为在实际的x86中,你不能在首次使用段选择器之前使用它,比如mov fs, eax,这是很慢的。 - Peter Cordes
无论如何,我不认为这可能没有额外的开销,即使你重新设计x86分段,甚至比386更多,这样你就不会受到一次只有几个段处于活动状态的限制,而是可以使用通用寄存器中的段选择器值作为指针。 这将在任何良好性能的情况下增加硬件复杂性,而且大概386已经很难仅仅在页表上花费晶体管了。(或者这也可能完全替代分页,但那将使x86成为具有分页方式与众不同的奇怪特殊ISA。) - Peter Cordes
作为一种真正简单的方法,像8086一样将32位值向左移动4位,那么您的地址空间就是64GiB而不是4。请注意,一些Java实现使用32位引用,这些引用被放大了8倍以产生35位地址,因此这几乎是相同的。作为一种更好的方法,使用顶部4位选择16个可配置段组之一,并按每个组基础上可配置的值来缩放剩余的28位。 - supercat
@PeterCordes:这种方法不会提供边界检查,但可以使用32位引用而不是64位。采用段描述符方法需要具有硬件缓存段描述符,但如果每个段的前几个字节与描述符共享高速缓存行,则需要访问对象头部之前访问最近未访问的对象的代码(在.NET等VM中常见)将同时获取所需信息和缓存描述符,从而将开销最小化。 - supercat
好的,那可能行得通,但肯定不是零开销。要在段内实现字节寻址,需要使用类似[ebx + eax]这样的2个寄存器寻址模式,其中基础地址被视为一个段地址,而索引则被视为一个字节偏移量。对于静态对象,[disp32 + idx]可以是静态段内的字节偏移量,但[disp32 + base]可以将disp32作为一个字节偏移量(超出disp8范围),以基础地址作为段地址。不知道8086的lea指令会如何扩展。看起来它会使386成为一个困难的编译器目标,尤其是当时的编译器。 - Peter Cordes
还是您建议这种新的分段方式可以选择性地使用,当前模型作为另一个选项(使用传统的段寄存器,并将它们全部设置为base=0/limit=4G,可以获得平面分页虚拟地址空间)。如果您想在答案中加入一些评论,我会取消我的反对票。特别是如果您删除了它可能是零开销的建议。您的第一个reg<<4的想法似乎仍然会使指针增量始终消耗两个寄存器,除非增量是按段粒度进行的。(我猜您会展开以节省寄存器...) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接