20

我只是不明白。任何手册都太技术性了。什么是平面内存和分段内存?寻址内存的方式,在内存中组织字节的方式?其中哪一个最适合 32 位计算机?谁能解释一下?实模式和保护模式与平面或分段内存有什么关系?谢谢!

4

2 回答 2

20

如果您只对在现有 32/64 位操作系统上运行的应用程序感兴趣,您可以简单地忘记分段内存。在 32 位操作系统上,您可以假设您有 4 GB 的“平坦”内存空间。Flat 意味着您可以使用 32 位值和寄存器来操作地址,正如您所期望的那样。

在 16 位处理器上,我相信一个地址是 20 位宽,你不能将它存储在一个寄存器中,所以你必须将一个基数存储在一个寄存器中,并且要指定一个实际地址,你必须添加一个偏移量那个基地。(如果我没记错的话,基数乘以 16,然后加上偏移量得到实际地址。)这意味着您一次只能寻址 64 KB;内存必须以 64 KB 块“分段”。

老实说,我认为初学者仍然听说它的唯一原因是很多旧的 16 位教程和书籍仍然存在。真的不需要了解程序在汇编级别是如何工作的。现在,如果您想学习操作系统开发,那就另当别论了。由于 PC 以 16 位模式启动,因此您至少需要学习足够的知识才能激活平面 32 位模式。

刚刚注意到您还询问了实模式与保护模式。实模式是 MS DOS 使用的模式。任何程序都可以访问任何硬件功能,例如直接与显卡的控制器对话以打印某些东西是很常见的。它没有引起任何问题,因为它不是多任务操作系统。

但是在任何现代操作系统上,普通程序都不会直接访问硬件,它们甚至不会直接访问内存。操作系统管理硬件并决定哪个进程在处理器上运行。它还为每个进程管理一个虚拟地址空间。这种功能在保护模式下可用,我相信它是 386 附带的,它是 PC 的第一个 32 位处理器。

于 2012-06-17T19:36:18.607 回答
11

使用地址(内存、I/O、内存映射 I/O 等)访问某些内容的指令有时会提供完整的(从处理器执行层的角度来看)地址,有时它们会提供偏移量。例如,您的近距离或相对跳转程序计数器是基地址,而指令提供了该基地址的偏移量,将两者相加,您将获得地址(在该级别)。

采用 16 位系统,其中有 16 位寄存器和 64KByte 的最大地址空间限制。扩展该内存的一种非常简单的方法是分段。指令中的寄存器不是包含整个地址的寄存器,而是包含一个基址的偏移量,很像相对于 pc 的指令。除了在这种情况下,还有另一个寄存器用作基地址。您可以在许多希望轻松扩展其地址范围而无需对内核进行太多修改的架构中看到这一点。(可以在内存控制器中完成,无需修改内核)在 x86 的情况下,有几个寄存器。一个用于扩大执行范围,分支。另一个扩展数据访问、加载和存储的范围。非 pc 相对分支的地址是使用左移 4 位的代码段计算的,然后添加到指令中指定的寄存器中。对于与 pc 无关的加载和存储,使用了数据段寄存器,左移 4 位添加指令中指定的寄存器。所以如果你想寻址0x123456789,你可以让段寄存器包含0x12340000,用于寻址的寄存器包含0x56789,或者段0x12345678和gpr包含0x9。pc相对寻址当然是segment + pc + offset。所以如果你想寻址0x123456789,你可以让段寄存器包含0x12340000,用于寻址的寄存器包含0x56789,或者段0x12345678和gpr包含0x9。pc相对寻址当然是segment + pc + offset。所以如果你想寻址0x123456789,你可以让段寄存器包含0x12340000,用于寻址的寄存器包含0x56789,或者段0x12345678和gpr包含0x9。pc相对寻址当然是segment + pc + offset。

这导致采用各种内存模型。小,小,中,大,大。您可以想象最小的模型将具有规则或假设在 x86 的情况下所有内容都在 64K 内存空间内,编译器和您的代码永远不必担心段寄存器,假设它们保持不变。对于较大的模型或当使用远指针到达更远没什么大不了时,您设置数据段,然后设置数据偏移并执行加载或存储。对于代码,您可以想象它有点困难,因为一旦您更改代码段寄存器,它就会影响您获取指令的整体地址。您可能需要一个硬件解决方案来允许分支修改段和偏移量,或者您可以在代码中进行(如果硬件允许)。我暂时不会把你和那个混为一谈。

每当您在代码中有一个数组时:

unsigned char abc[123];

那基本上是一样的。基地址,数组在内存中开始的地址就像你的段,索引是你的偏移量。如果上面的 abc 位于地址 0x1004,则 abc[5] 位于地址 0x1004+5 = 0x1009。不像 x86 段:偏移地址那样移动,而是添加基数和偏移量的相同概念。您没有添加的某些分段架构,某些寄存器中的某些位是高位。在这些系统之一上获取地址 0x12345,0x1 必须在段中,0x2345 在 16 位 gpr 中。您可以将其视为移位并根据需要添加,但与 x86 段:偏移不同,您也可以将其视为移位和或。

平坦的内存空间有点像错觉,尤其是在 x86 系统中。x86 计算机,32 位甚至许多 64 位,将插入卡的平面内存空间限制为总共 1Gig,对于总共有 4 Gig 地址空间的 32 位系统来说很有意义,并且这就是为什么其中一些给你一个 3 gig 的限制,或者给你 4 gig 的错觉,但是为了插卡而砍掉了一些。(您在主板上的许多项目以及实际的插卡都在这个空间中)。根据视频卡和分辨率等,您有时无法将整个帧缓冲区放入该外围空间的子集中,因此您必须对访问进行分段。BIOS 可能已将地址 0x80000000 作为 x86 地址空间中的基地址,然后在视频卡的其他寄存器中指定视频卡地址空间内的地址。出于演示目的,假设您在 x86 地址 0x80000000 处获得了一个 16MByte 的窗口。16Mbytes 是 0x01000000。如果您想访问视频内存中的地址 0x04321888,您可以想象必须将视频卡中的段寄存器设置为 0x04,然后在 x86 地址空间(也是 pci(e) 地址空间)中使用地址 0x80321888。

这里的底线是从这里取一些位,从那里取一些位,把它们放在一起,这就是目标的地址。在处理外设时,无论是显卡还是板载 I/O 控制器,或者 pci 或 pcie 控制器,您都必须学会从目标地址空间的角度进行思考。从您的程序的角度来看,处理器有一个地址空间。mmu 可以并且确实将其打乱到物理地址空间中,然后您拥有您的 pcie 地址空间,然后通过 pcie 访问的外围设备拥有自己的地址空间。英特尔和基于英特尔的 pc 世界所做的是使处理器的物理地址空间和 pcie 地址空间相同。mmu 中的虚拟与物理加扰仍然存在,进入外设地址空间的窗口仍然存在,

真实和受保护与访问有关。例如,在 C 中,您可以创建指针、更改指针并创建您想要的任何地址,这是否意味着您可以在另一个应用程序内存或内核内存中四处寻找?理想情况下,您不想让这种情况发生,因此当您为该应用程序执行指令时,您在一个虚拟机中的每个应用程序,如果您愿意,每个内存访问(无论是代码还是指令)都会经过过滤器。该过滤器检查该访问是否在程序允许的空间内,如果它超出该空间,则发生异常(认为中断)该异常允许内核(没有这些限制或具有不同的限制)决定允许该访问,或者虚拟化对某物的访问,或向用户发出警告(一般保护故障)。以像 vmware 这样的实际虚拟机程序为例,允许虚拟化程序在处理器上实际运行指令,当该虚拟化程序访问它认为是显卡的地址时,发生保护故障,vmware 驱动程序/应用程序(想想内核级别)获取该地址并伪造视频卡响应并将控制权返回给应用程序。让指令在金属上执行允许更快的虚拟化,而不是模拟每个处理器指令。这是极端情况,即使您正在阅读本文的 Web 浏览器也已被虚拟化,因此它认为它有一个基于诸如 0x000 或 0x8000 之类的基地址的内存空间,您将特定操作系统的每个程序编译到相同的平面虚拟内存空间,操作系统负责将地址从虚拟地址更改为物理地址。您的网络浏览器对其地址 0x8000 的访问可能是物理地址 0x12345678,而您的 mp3 播放器程序 0x8000 访问可能是物理地址 0x2345678,但对于这两个应用程序,它们的指令都在计算 0x8000。

问什么是最好的总是一个相对的术语,一个人最好是另一个人最差。你必须为自己定义最好的和最坏的。势头和公众舆论将 x86 推向了平坦的内存空间,或者至少从程序员的角度来看是平坦的内存空间的错觉,因此您在顺应潮流时会遇到更少的麻烦。

我建议购买一份 8086/8088 编程器和硬件参考手册,几块钱就能买到。

http://www.amazon.com/Manual-Programmers-Hardware-Reference-240487-001/dp/1555120814/ref=sr_1_1?ie=UTF8&qid=1340000636&sr=8-1&keywords=8088+programmers+reference

拿一个像 pcemu 这样的模拟器(我有一个为此目的的克隆http://github.com/dwelch67/pcemu_samples) 并使用指令集,在虚拟化、保护等之前的老派。回到上面计算的段和偏移量时,将段左移四位并添加偏移量(这在本手册中都有描述)。从那以后,每一代人都在尝试改进,同时尝试反向兼容。这当然帮助了利润,但把处理器变成了讨厌的野兽。你最好忘记 x86 的细节并学习一些更干净的系统,因为 x86 的发展方式,你将获得最小的收益,例如尝试用 asm 编写一些东西而不是编译的代码。由于来自不同系列的处理器以不同的速度执行相同的代码,因此新一代通常以更慢的速度执行来自先前的手动调整的代码。您不能手动调整某些东西以在所有平台上快速运行,而不是比编译器更快,因此只需将 x86 asm 代码留给编译器即可。在没有这些问题的健全平台上工作,并且可以根据需要进行调整或制作更好的编译器等。

于 2012-06-18T06:43:16.460 回答