它是80x86系列中的第一种32位微处理器,而且制造工艺也有了很大的进步,与80286相比,80386内部内含27.5万个晶体管,时钟频率为12.5MHz,后提高到20MHz,25MHz,33MHz。80386的内部和外部数据总线都是32位,地址总线也是32位,可寻址高达4GB内存。它除具有实模式和保护模式外,还增加了一种叫虚拟86的工作方式,可以通过同时模拟多个80x86处理器来提供多任务能力。除了标准的80386芯片,也就是80386DX外,出于不同的市场和应用考虑,INTEL又陆续推出了一些其它类型的80386芯片:80386SX、80386SL、80386DL等。
80386最经典的产品为80386DX-33MHz,一般我们说的80386就是指得它。针对内存的速度瓶颈,英特尔为80386设计了高速缓存(Cache),采取预读内存的方法来缓解这个速度瓶颈。本来最初的设计,80386将内置L1 Cache,但由于工艺、成本、工期等等方面的限制,80386最后并没有内置L1 Cache,而是将专门开发的L1 Cache芯片放置在CPU之外的主板上,但从此以后,Cache就和CPU成为了如影随形的东西。另外,80386的内存管理非常先进,有页式、段式、段页式三种管理方式,可以管理巨大的内存空间,从而为应用程序提供足够的舞台.三存内存管理方式实际上就是对应分段部件、分页部分的各自启用和同时启用。
如图所示, 从算术逻辑单元(ALU)中出来两路32位地址线,分别用来读源操作数据和目的操作数,地址首先发给分段部件,如果分段部件没有启用,就会透明发给分页部件。ALU发出的地址称为逻辑地址,逻辑地址输入给分段部件后,输出线性地址(在Linux中也叫虚拟地址),线性地址输入给分页部件后,分页部件输出物理地址。
分页部件将内存整齐地划分为4K大小的页框,并将这将页框组件成二级目录结构。一个线性地址也被理解为由三部分组成,即目录索引、页表索引和页内偏移。
• 目录索引PD(Page Derectory)
• 页表索引PT(Page Table)
目录索引从目录表中中取得一个目录项,目录项是一个指向页表的指针,页表索引从页表中取得一个页表项,页表项是一个页框编号,分页部件通过页框编号,页框编号与页内编移组合得到线性地址对应的实际物理地址。
通常把各级页目录和页表泛称为页表,页表和段描述符表一样是每个进程一份,每个进程在恢复执行时,将页表的首地址放在CR3寄存器里。
CPU在遇到一个访存指令时,逻辑地址翻译成线程地址是非常简单的,具体就是将当前数据段/指令段/附加段选择子中段基址与指令中的偏移地址相加即可。由于段选择子对应的段描述符内容已经加载到段选择子对应的影子寄存器中,这个过程中是不会涉及访问内存来取得段描述符的。CPU在一定时间内只会访问内存段选择子对应的段描述符所描述的段内的数据,当需要段切换时,程序会显式修改CS/DS/ES寄存器。
线性地址翻译成物理地址则不然,由于程序访问的内存在哪个物理页上是完全不能预测的,因而线程地址翻译成物理地址时是需要去内存中访问对应的页表项(或页目录项)的。要访问一个内存就必需先访问另一个内存是很搞笑的。因而CPU中增加了一个TLB,即Translation Lookaside Buffer。TLB就是对页表的一个高速缓存,相当于一段专用的Cache。由于程序的运行也有一定的局部性,因则TLB hit的情况非常高。虽然如此,在TLB miss时,CPU仍然需要去内存中加载页表以确定这个线程地址对应的物理地址是什么。
TLB的条目的数量较少,80386有32个。每一个TLB寄存器的每个条目包含一个页面的信息:有效位,虚页面号,修改位,保护码,和页面所在的物理页面号,它们和页面表中的表项一一对应。
由于TLB是页表的缓存,每个进程都有不同的页表,CR3存储着当前进程的页表道地址,故进程切换时会重新装载CR3,重新装载CR3时,TLB里面的内容会失效。CR3的内容装载前后一样的情况下,TLB的刷新也会发生,因为常用这种方式来刷新TLB。
80386仅支持两级页表结构,因为当时的内存还都很小。4M 内存就算大的了(是Windows 95的最低要求),运行DOS只需要1M。Pentium Pro下分页可以是三级。
一个两级页表的组织方式如下图所示:
给定一个线性地址被分成3部分,DIR, TABLE, OFFSET。
翻成物理地址分三步,(1)首先CR3是页目录的启始地址,CR3+DIR处即页表的启始地址,(2)从页表中拿TABLE去索引,得到页表项,(3)从页表项中取得bit 12~31为物理地址的高24位地址,拿线性地址的低12位即OFFSET做为物理地址的低12位,组成一个32位物理地址。
一个32位的页表项中除高24位为物理地址的高32位(也是页框号),还有12位,被用做一些标记位。用来标记当前页框是否是脏的,是否可读可写,是用户内存还是系统内存,该内存有没有被访问过等。
Linux内核是最期采用3级页表,2.6.11之后采用4级页表,原理与两级页表一样。
对于支持多级页表的处理器,OS可以通过配置寄存器来选择自己支持几级页表,每一级用线程地址中的几个位来表示。(具体是哪个寄存器暂时没找到资料,后面补上)。
段式存储中,段描述符表的首地址是加载到LDTR寄存器中的。页式内存管理中,页表的首地址是加载到CR3寄存器中的。
分段部件可以通过CR0的bit 0来使能,分页部件则是通过CR0的bit 31来使能。两者都使能则是段页式内存管理方式。
CR0的低16位包含了与80286的MSW一致的位定义,保持了和80286的兼容,同时也兼容了从80286开始的两条指令LMSW/SMSW。指令LMSW和SMSW分别用于装入和保存机器状态字信息,可以通过MOV指令对CR0进行读写操作。CR0中各位含义如下:
PE(Protection Enable)保护模式允许位,用来启动CPU进入虚地址保护方式。PE=0表示CPU工作在实地址方式;PE=1表示CPU工作在虚地址保护方式。
MP(Monitor Coprocessor)监控协处理器,MP=1表示协处理器在工作;MP=0表示协处理器未工作。
EM(Emulation)协处理器仿真,当MP=0,EM=1时,表示正在使用软件仿真协处理器工作。
TS(Task Switched)任务转换,每当进行任务转换时,TS=1;任务转换完毕,TS=0。TS=1时不允许协处理器工作。
ET(Extension Type)处理器扩展类型,反映了所扩展的协处理器的类型,ET=0为80287,ET=1为80387。
PG(Paging)页式管理机制使能,PG=1时页式管理机制工作,否则不工作。
NE(Numeric Error)数值异常中断控制,NE=1时,如果运行协处理器指令发生故障,则用异常中断处理,NE=0时,则用外部中断处理。
WP(Write Protect)写保护,当WP=1时,对只读页面进行写操作会产生页故障。
AM(Alignment Mask)对齐标志,AM=1时,允许对齐检查,AM=0时不允许,关于对齐,在EFLAGS的AC标志时介绍过,在80486以后的CPU中,CPU进行对齐检查需要满足三个条件,AC=1、AM=1并且当前特权级为3。
NW(Not Write-through)和CD(Cache Disable),这两个标志都是用来控制CPU内部的CACHE的,当NW=0且CD=0时,CACHE使能,其它的组合比较复杂。
前4个定义从80286开始,接着的2个定义从80386开始存在, 后面4个是从80486开始定义的。
CR1寄存器用来保留给Intel微处理器将来开发使用;
CR2寄存器包含一个32位的线性地址,指向发生最后一次也故障的地址,只有在PG=1时,CR2才有效,当页故障处理程序被激活时,压入页故障处理程序堆栈中的错误码提供页故障的状态信息;
CR3寄存器中包含页物理目录表的物理基地址,由于每4KB为一页,80386中的页目录表总在页的整数边界上,CR3的低13位总是为0,只有当CR0中的PG=1时,CR3的页目录基地址才有效。