分段内存与平坦内存

ali*_*ali 15 memory assembly flat

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

Bas*_*ard 18

如果您只对在现有32/64位操作系统上运行的应用程序感兴趣,则可以简单地忘记分段内存.在32位操作系统上,您可以假设您有4 GB的"平坦"内存空间.Flat意味着您可以按照预期操作具有32位值和寄存器的地址.

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

说实话,我认为初学者仍然听到这个的唯一原因是因为很多旧的16位教程和书籍仍然存在.实际上不需要了解程序在程序集级别的工作方式.现在,如果您想学习OS开发,那就是另一个故事.由于PC以16位模式启动,因此您至少需要学习能够激活平坦的32位模式.

刚刚注意到你还询问了真实模式与保护模式.实模式是MS DOS使用的模式.任何程序都可以访问任何硬件功能,例如,通常直接与显卡的控制器通信以打印某些内容.它没有引起任何问题,因为它不是一个多任务操作系统.

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


old*_*mer 9

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

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

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

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

unsigned char abc[123];
Run Code Online (Sandbox Code Playgroud)

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

平坦的内存空间有点像幻觉,特别是在x86系统中.x86计算机,32位甚至许多64位,限制插入卡的平面存储空间总量为1Gig,对于总共有4位地址空间的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,您将特定操作系统的每个程序编译为相同平面虚拟内存空间和操作系统负责将地址从虚拟更改为物理.您的Web浏览器访问其地址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代码留给编译器.在没有这些问题的理智平台上工作,如果你愿意,可以调整,或者做一个更好的编译器等.