为什么要(不)细分?


42

我正在研究操作系统和x86体系结构,当我阅读有关分段和分页的内容时,我自然地很好奇现代OS如何处理内存管理。从我的发现中,Linux和大多数其他操作系统从本质上回避了分段,而支持分页。我发现这样做的一些原因是简单性和可移植性。

分段(x86或其他方式)有什么实际用途,我们是否会看到使用它的健壮操作系统,或者它们将继续支持基于分页的系统。

现在,我知道这是一个加载的问题,但是我很好奇如何使用新开发的操作系统来处理分段。支持分页是否有意义,以至于没有人会考虑采用一种“分段”的方法?如果是这样,为什么?


当我说“避免”分割时,我的意思是Linux仅在需要时使用它。用户和内核代码/数据段只有4个段。在阅读英特尔文档时,我感觉到细分的设计考虑了更强大的解决方案。然后我又在很多场合被告知x86可能过于复杂。


在链接到Linux Torvald最初针对Linux的“公告”后,我发现了这个有趣的轶事。他稍后在几篇文章中说:

简而言之,我想说移植是不可能的。它主要是用C语言编写的,但是大多数人不会称呼我写C语言。它使用了我能找到的386的所有可能的功能,因为它也是一个向我传授386的项目。如上所述,它使用了MMU ,用于分页(尚未到磁盘)和分段。正是这种细分使其真正依赖386(每个任务都有一个64Mb的代码和数据段-4Gb中最多64个任务。任何需要超过64Mb /任务的人-坚韧的cookie)。

我想我自己对x86的试验使我提出了这个问题。Linus没有StackOverflow,因此他只是实现了它来进行尝试。


你读了什么书?

1
我正在读书。在阅读《英特尔系统编程》手册(第3卷)时,我开始问自己这个问题,但是在“了解Linux内核”和其他在线资源中我读到了一些有关Linux内存管理的内容。
Shickadance先生2011年

特别是我正在阅读有关本地描述符表的部分,并且很好奇操作系统如何使用它们。
Shickadance先生2011年

1
OpenBSD结合了x86分段和分页功能来获得NX位模拟(安全功能,禁止执行数据页)。可能是PaX也使用了此功能。

我对此几乎一无所知。我只是输入搜索问题,以查看有关所有当前使用的操作系统的投诉的答案。纵观投诉,大多数人使用PC和平板电脑来完成一些特定任务。因此,为什么不分配更多的内存使用量来更快地执行这些任务,而不是给所有正在运行的外围废话访问。

Answers:


31

使用分段,例如,可以将每个动态分配的对象(malloc)放在其自己的内存段中。硬件将自动检查段限制,并且将消除整个安全漏洞(缓冲区溢出)类别。

同样,由于所有段偏移都从零开始,因此所有编译代码将自动与位置无关。调用另一个DLL可以归结为具有恒定偏移量的far调用(取决于所调用的函数)。这将大大简化链接程序和加载程序。

使用4个保护环,可以设计出更细粒度的访问控制(使用分页时,您只有2个保护级别:用户和管理员)和更强大的OS内核。例如,只有环0可以完全访问硬件。通过将核心操作系统内核和设备驱动程序分为环0和1,您可以制作一个更健壮和非常快速的微内核操作系统,其中大多数相关的访问检查将由硬件完成。(设备驱动程序可以通过TSS中的I / O访问位图访问硬件。)

但是x86有点受限制。它只有4个“空闲”数据段寄存器。重新加载它们非常昂贵,并且可以同时访问8192个段。(假设您要最大化可访问对象的数量,因此GDT仅保存系统描述符和LDT描述符。)

现在,在64位模式下,分段被描述为“传统”,并且仅在有限的情况下才进行硬件限制检查。恕我直言,一个大错误。实际上,我不怪英特尔,也怪大多数开发人员,大多数开发人员都认为分段“太复杂了”,并渴望获得平坦的地址空间。我还指责缺乏想象力来充分利用分段的OS编写者。(AFAIK OS / 2是唯一充分利用分段功能的操作系统。)


1
这就是为什么我把这个打开了。在这个问题上肯定会有一些不同的
看法

1
@zvrba:很棒的解释!!!对此表示感谢。现在我有一个疑问:您难道不认为INTEL可以通过使页面不重叠并在传呼的帮助下提供4GB的容量来赢得大奖吗?我的意思是,据我所知,“使用分页进行分段”只能处理最大4GB的虚拟内存地址空间。那就是“花生”!想象一下,能够有一个每个最大4GB的代码,堆栈,数据段,并且可以根据需要进行非重叠或重叠!那时,这将是一个巨大的成功,而不必像现在那样要求完整的64位架构。
幻想

1
为何细分效果很好的奇妙解释。可惜落在了路边,真是太可惜了。对于那些想了解更多信息的人,这里有更多细节
GDP2

1
难怪我喜欢OS / 2!由于无知和行销,真正有价值的技术损失惨重。
ylluminate

任何认为分段是个好主意的人都不能老到足以记住分段的糟糕程度。糟透了 几乎所有曾经编写的C代码都希望有一个平坦的地址空间。能够查看指针并仅查看其地址很方便,而不必钻入段基(假设即使有可能),除非在内核允许您看到,否则它不在x86保护模式下进行分段以某种方式,最有可能是通过非常昂贵的系统调用。除非您换出整个细分受众群,否则无法交换细分受众群。分页远胜于此。
doug65536

25

简短的答案是,分段是一种hack,用于使具有有限寻址能力的处理器超过这些限制。

对于8086,芯片上有20条地址线,这意味着它可以物理访问1Mb的内存。但是,内部架构基于16位寻址,这可能是由于希望保持与8080的一致性。因此,指令集包括段寄存器,该段寄存器将与16位索引结合以允许对整个1Mb存储器进行寻址。80286通过真正的MMU扩展了该模型,以支持基于段的保护和更多内存的寻址(iirc,16Mb)。

在PDP-11的情况下,处理器的更高型号提供了对指令空间和数据空间的分段,以再次支持16位地址空间的限制。

分段的问题很简单:您的程序必须显式地解决体系结构的限制。对于8086,这意味着您可以访问的最大连续内存块为64k。如果您需要访问更多内容,则必须更改段寄存器。对于C程序员来说,这意味着您必须告诉C编译器应生成什么样的指针。

对MC68k进行编程要容易得多,它具有32位内部体系结构和24位物理地址空间。


5
好吧,这一切都说得通。但是,阅读英特尔文档时,人们倾向于认为这些段实际上可以用于针对程序错误提供更高的硬件级别保护。特别是《系统编程指南》第3.2.3节-多段模型是否有优势?说Linux使用受保护的平面模型是否正确?(第3.2.2节)
Shickadance先生,2011年

3
自从我关注Intel内存架构的细节以来已经很长时间了,但是我认为分段式架构不会提供任何更大的硬件保护。MMU可以为您提供的唯一真正保护措施就是将代码和数据分开,以防止缓冲区溢出攻击。而且我相信,通过页面级属性,无需细分即可控制。从理论上讲,您可以通过为每个对象创建一个单独的段来限制对对象的访问,但是我认为这不合理。

1
谢谢,您已将所有受压抑的记忆带回分段存储器中进行图像处理-这将意味着更多的治疗方法!
马丁·贝克特

10
您完全误解了细分。在8086年,它可能是黑客。80286引入了保护模式,这对于保护至关重要;在80386中,它甚至进一步扩展了,并且段可以大于64kB,这仍然受益于硬件检查。(顺便说一句,80286没有MMU。)
zvrba 2011年

2
早在1985年推出386时,一个4 GiB地址空间就被认为是巨大的。请记住,当时20 MiB的硬盘非常大,对于仅装有软盘驱动器的系统来说,这种情况并不少见。3.5英寸FDD于1983年推出,其格式化后的容量高达360 KB。(1986年提供了1.44 MB 3.5英寸FDD。)由于实验错误,当时每个人都想到了32位地址空间,因为我们现在认为64位:物理上可接近,但很大,实际上是无限的。
CVn

15

对于80x86,有4个选项-“无”,仅分段,仅分页以及分段和分页。

对于“无”(没有分段或分页),您最终得不到一种简单的方法来保护进程自身,没有简单的方法来保护进程彼此之间,没有办法处理诸如物理地址空间碎片之类的事情,没有办法避免位置尽管存在所有这些问题,但在某些情况下(理论上)它可能还是有用的(例如,仅运行一个应用程序的嵌入式设备;或者可能使用JIT并始终虚拟化所有内容的某种设备)。

仅用于细分;它几乎解决了“保护进程免受自身侵害”的问题,但是当一个进程想要使用8192个以上的段(假设每个进程使用一个LDT)时,要使其可用就需要很多变通方法,这使得它大部分都被破坏了。您几乎解决了“互相保护过程”的问题;但是以相同特权级别运行的不同软件可以加载/使用彼此的段(有解决此问题的方法-在控制传输和/或使用LDT时修改GDT条目)。它还主要解决了“位置无关的代码”问题(它可能导致“段相关的代码”问题,但意义不大)。对于“物理地址空间碎片”问题,它没有任何作用。

仅用于分页;它并不能解决“保护进程免受自身侵害”的问题(但是,坦白地说,这只是调试/测试使用不安全语言编写的代码的真正问题,而且还有更强大的工具,如valgrind)。它彻底解决了“相互保护过程”的问题,彻底解决了“位置无关代码”的问题,彻底解决了“物理地址空间碎片化”的问题。另外,它还提供了一些非常强大的技术,如果不进行分页,这些技术几乎不可行。包括“写入时复制”,内存映射文件,有效的交换空间处理等内容。

现在,您认为同时使用分段和分页将为您带来两者的好处;从理论上讲,它可以做到,除了您从分段中获得的唯一好处(分页无法获得更好的收益)是解决“保护进程不受自身影响”的问题的解决方案,这一问题没人真正关心。实际上,您得到的是两者的复杂性和两者的开销,却几乎没有收益。

这就是为什么几乎所有为80x86设计的操作系统都不使用分段进行内存管理的原因(它们确实将分段用于按CPU和按任务存储之类的功能,但这主要是为了方便起见,以避免为此消耗更多有用的通用寄存器)东西)。

当然,CPU制造商并不傻-他们不会花时间和金钱来优化他们不知道使用的东西(他们将优化几乎每个人都使用的东西)。因此,CPU制造商没有优化分段,这使得分段速度比预期的要慢,这使得OS开发人员希望避免这种情况。通常,它们仅保留分段以实现向后兼容性(这很重要)。

最终,AMD设计了长模式。无需担心旧的/现有的64位代码,因此(对于64位代码)AMD消除了尽可能多的分段。这为OS开发人员提供了另一个避免继续分段的理由(没有简便的方法将用于分段的代码移植到64位)。


13

令我惊讶的是,自发布此问题以来的所有时间里,没有人提到分段存储器架构的起源以及它们所能提供的真正功能。

最初的系统发明了或精炼成有用的形式,围绕着分段分页虚拟内存系统(以及对称的多重处理和分层文件系统)的设计和使用的所有功能,都是Multics(另请参见Multicians网站)。分段内存使Multics可以向用户提供所有内容都在(虚拟)内存中的视图,并允许最终共享所有内容直接形式(即直接在内存中寻址)。文件系统仅是到内存中所有段的映射。当以系统的方式正确使用时(例如在Multics中),分段内存使用户摆脱了管理二级存储,共享数据以及进程间通信的许多负担。其他答案使人难以置信,声称分段存储器更难使用,但这不是真的,Multics在几十年前取得了巨大的成功。

英特尔创建了分段内存的低端版本80286,尽管该功能非常强大,但其局限性使其无法用于任何真正有用的功能。80386改进了这些限制,但是当时的市场力量几乎阻止了任何可以真正利用这些改进的系统的成功。从那以后的几年中,似乎所有的人都学会了忽略过去的教训。

英特尔还尽早尝试构建功能更强大的超微型设备,称为iAPX 432,当时它已经远远超过了其他任何东西,并且它具有分段存储器架构和其他强烈面向对象编程的功能。但是,最初的实现速度太慢,因此没有进一步尝试对其进行修复。

可以在Paul Green的论文Multics Virtual Memory-Tutorial and Reflections中找到有关Multics如何使用分段和分页的更详细讨论。


1
很棒的信息和精湛的论点。感谢链接,它们是无价的!!!
幻想

1
感谢您链接到Multics和非常有用的答案!显然,细分在许多方面都优于我们现在所做的。
GDP2

1
您的答案是真正的宝石。非常感谢您分享我们丢失的这些见解。我渴望看到通过开发适当的操作系统(可以促进硬件的完善)而回归到细分市场。这种方法确实可以解决很多问题!甚至听起来好像我们可以以更高的性能水平和带有分段的裸机获得真正的OOP语言。
ylluminate

6

分段是一种破解/解决方法,可以通过16位处理器处理多达1MB的内存-通常只能访问64K的内存。

当32位处理器问世时,您可以使用平面内存模型寻址多达4GB的内存,并且不再需要分段-分段寄存器已重新用作保护模式下GDT /分页的选择器(尽管您可以具有16位保护模式)。

扁平存储器模式对于编译器来说也更加方便-您可以用C编写16位分段程序,但这有点麻烦。平面内存模型使一切变得简单。


当我们只能使用分页时,关于分段所提供的“保护”还有很多要说的吗?
Shickadance先生,2011年

1
@先生。Shickadance Segmentation不提供任何类型的内存保护-对于内存保护,您需要保护模式,在此模式下,您可以使用GDT或页面调度来保护内存。
贾斯汀

5

某些体系结构(如ARM)根本不支持内存段。如果Linux在某些方面依赖于源代码,那么就不可能很容易地将其移植到这些体系结构中。

从更广泛的角度来看,内存段的失败与C和指针算法的持续流行有关。在具有平面内存的体系结构上,C开发更加实用。如果需要平面内存,则选择内存分页。

大约在80年代之交,英特尔作为一个组织,正期望Ada和其他高级编程语言在未来的普及。从根本上讲,这是他们一些更为壮观的故障的来源,例如可怕的APX432和286内存分段。有了386,他们屈服于平面内存程序员。分页并添加了TLB,并将分段调整为4GB。然后AMD基本上通过将基本reg设置为dont-care / implied-0(使用fs?

话虽如此,内存段的优点是显而易见的-无需重新填充TLB即可切换地址空间。也许某天某人会制造出具有竞争力的,支持分段的CPU,我们可以为其编写面向分段的OS,程序员可以使Ada / Pascal / D / Rust /另一种不需要的语言-内存程序。


1

细分是应用程序开发人员的沉重负担。这就是消除细分的巨大推动力。

有趣的是,我经常想知道如果Intel放弃对这些旧模式的所有旧有支持,那么i86会更好。在此更好地意味着更低的功率并可能更快地运行。

我猜可能有人会争辩说,英特尔用16位段使牛奶变酸,从而导致开发人员大反叛。但是,让我们面对现实吧,尤其是当您查看现代应用程序时,64k地址空间已算是什么。最后,他们不得不做些什么,因为竞争可以并且确实针对i86的地址空间问题进行了市场营销。


1

细分会导致页面翻译和交换变慢

由于这些原因,分段在x86-64上被大大降低。

它们之间的主要区别在于:

  • 分页将内存分成固定大小的块
  • 分割允许每个块具有不同的宽度

尽管具有可配置的段宽度可能看起来更聪明,但是随着您增加进程的内存大小,碎片是不可避免的,例如:

|   | process 1 |       | process 2 |                        |
     -----------         -----------
0                                                            max

最终将随着流程1的增长而变得:

|   | process 1        || process 2 |                        |
     ------------------  -------------
0                                                            max

直到不可避免的分裂:

|   | process 1 part 1 || process 2 |   | process 1 part 2 | |
     ------------------  -----------     ------------------
0                                                            max

这一点:

  • 转换页面的唯一方法是对进程1的所有页面进行二进制搜索,这将导致无法接受的log(n)
  • 过程1第1部分的交换可能很大,因为该部分可能很大

对于固定大小的页面:

  • 每个32位转换仅读取2个内存:目录和页表遍历
  • 每次交换都是可接受的4KiB

固定大小的内存块更易于管理,并且主导了当前的OS设计。

另请参阅:https : //stackoverflow.com/questions/18431261/how-does-x86-paging-work

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.