仍是把博文“CPU的内部架构和工做原理”里的这个表搬出来,祭奠那些不该该在岁月的洪荒中被淹埋的荣耀:
html
8086的诞生,标志着Intel 正式进入了x86时代,这是个多么具备记念意义的日子:1978-6-8。同时,8086的诞生也是处理器内存寻址技术的第一次飞跃。 git
对于一根实际的、实实在在的、物理的、可看得见、摸得着的内存条而言,处理器把它当作8位一个字节的序列来管理和存取,每个内存字节都有一个对应的地址,咱们叫它物理地址,用地址能够表示的长度叫作寻址空间。而CPU是如何去访问内存单元里的数据的方式就叫作寻址。 算法
8086得CPU在内存寻址方面第一次引入了一个很是重要的概念----段。在8086以前都是4位机和8位机的天下,那是并无段的概念。当程序要访问内存时都是要给出内存的实际物理地址,这样在程序源代码中就会出现不少硬编码的物理地址。这样的程序可想而知,难重定位,可控性弱,结构丑陋,那个年代写这样的程序在咱们如今看来是多么让人恼火的一件事儿。 数据结构
8080问世后四年也就是1978年,Intel开始设计16位CPU,正常来讲8086的寻址空间应该是216=64KB才对,但Intel就恰恰不这干,8086的目标寻址空间直指1M,也就是说8086的地址总线位宽要达到20位。如何让16位的内部寄存器对20位的外部地址空间进行寻址,Intel的工程师们从当时的PDP-11小型机身上找到了灵感。PDP-11是美国迪吉多电脑(Digital Equipment Corp.)公司于1970到1980年代热销的16位迷你电脑,PDP-11的内存管理单元(MMU)能够将16位地址映射到24位地址空间里(至于人家是怎么弄,我就真不晓得了)。 架构
为了支持分段机制,Intel在8086的CPU里新增了4个寄存器,分别是代码段CS,数据段DS,堆栈段SS和其余ES(之后深刻介绍一下这几个兄弟伙,这涉及到进程的在内存的运行状况)。这样一来,一个物理地址就由两个部分组成,分别是“段地址”:“段内偏移量”。例如,ES=0x1000,DI=0xFFFF,那么这个数据ES:DI在内存里的绝对物理地址就是: ui
AD(Absolute Address)=(ES)*(0x10)+(DI)=0x1FFFF 编码
就是讲段基地址左移4位而后加上段内偏移量就获得了物理内存里的绝对地址,通过这么一个变换,就能够获得一个20位的地址,8086就能够对20位的 1M 内存空间进行寻址了。以下: 很明显,这种方式能够寻址的最高地址为0xFFFF:0xFFFF,其地址空间为0x00000~0x10FFEF,由于8086的地址总线是20位,最大只能访问到1MB的物理地址空间,即物理地址空间是0x00000~0xFFFFF。当程序访问0x100000~0x10FFEF这一段地址时,由于其逻辑上是正常的,CPU并不会认为其访问越界而产生异常,但这段地址确实没有实际的物理地址与其对应,怎么办?此时CPU采起的策略是,对于这部分超出1M地址空间的部分,自动将其从物理0地址处开始映射。也就是说,系统计算实际物理地址时是按照对1M求模运算的方式进行的,在有些技术文献里你会看到这种技术被称之为wrap-around。仍是经过一幅图来描述一下吧:
spa
根据前面的讲解咱们能够发现段基址有个特征,其低4位全为0,也就是说每一个段的起始地址必定是16的整数倍,这是分段的一个基本原则。这样每一个段的最小长度是16字节,而最大长度只能是64KB。这里咱们能够计算一下,1MB的物理地址空间能划分红多少个段。 操作系统
若是每一个段的长度为16字节,这样1MB物理地址空间最多能够划分红64K个段; .net
若是每一个段的长度为64KB,那么1MB的物理地址空间最多能划分红16个段。
8086这种分段基址虽然实现了寻址空间的提高,可是也带来一些问题:
1、同一个物理地址能够有多种表示方法。例如0x01C0:0x0000和0x0000:0x1C00所表示的物理地址都是0x01C00。
2、地址空间缺少保护机制。对于每个由段寄存器的内容肯定的“基地址”,一个进程老是可以访问今后开始64KB的连续地址空间,而没法加以限制。另外一方面,能够用来改变段寄存器内容的指令也不是什么“特权指令”,也就是说,经过改变段寄存器的内容,一个进程能够为所欲为地访问内存中的任何一个单元,而丝绝不受限制。不能对一个进程的内存访问加以限制,也就谈不上对其余进程以及系统自己的保护。与此相应,一个CPU若是缺少对内存访问的限制,或者说保护,就谈不上什么内存管理,也就谈不上是现代意义上的中央处理器。
总结一下:8086和后来的80186,这种只能访问1MB地址空间的工做模式,咱们将其称之为“实模式”。个人理解就是“实际地址模式”,由于经过段基址和段偏移算出来的地址,通过模1MB以后得出来的地址都是实际内存的物理地址。
因为8086的上述问题,1982年,Intel在80286的CPU里,首次引入的地址保护的概念。也就是说80286的CPU可以对内存及一些其余外围设备作硬件级的保护设置(实质上就是屏蔽一些地址的访问)。自从最初的x86微处理器规格之后,它对程序开发彻底向下兼容,80286芯片被制做成启动时继承了之前版本芯片的特性,工做在实模式下,在这种模式下其实是关闭了新的保护功能特性,所以能使以往的软件继续工做在新的芯片下。后续的x86处理器都是在计算机加电启动时都是工做在实模式下。
也就是说,在保护模式下,程序不能再随意的访问物理内存了,有些内存地址CPU作了明确的保护限制。
1985年80386的问世,使Intel完成了从16位到32位CPU的飞跃,这中间80286毫无疑问的就成了此次飞跃的跳板。80286的地址线已经达到24位,可寻址空间是16MB,但Intel当初设计80286时提出的目标是向下兼容,这也是Intel一向的做风,正是这种做风为Intel后面设计80386时增添了几根儿烦恼丝。因此,在“实模式”下,80286所表现的行为和8086所表现的彻底同样。
80386是32位CPU,也就是说它的ALU数据总线是32位,地址总线的位宽和CPU内部数据总线的位宽是一致的,都是32位,其寻址范围可达4GB。若是从新设计80386的架构,其结构应该至关简洁才对。可是80386却很遗憾的没法作到这一点,做为一个产品系列中的成员分子,80386必须继续维持“前辈”们的那些段寄存器,必须支持实模式,同时还要支持保护模式。能够看得出来,80386其实也不容易。
因此,Intel决定在80386的段寄存器(CS,DS,SS,ES)的基础上构筑保护模式,而且继续保留段寄存器为16位,同时又增添了两个段寄存器FS和GS。显然,为了实现保护模式,光是用段寄存器来肯定一个基地址是不够的,至少还要有一个地址段的长度,而且还须要一些诸如访问权限之类的其余信息。因此,这里须要的是一个数据结构(这个数据结构就叫作“段描述符”,之后会看到),而并不是一个单纯的基地址。对此, Intel设计人员的基本思路是:
在保护模式下改变段寄存器的功能,使其从一个单纯的段基址变成指向一个“段描述符”的指针。所以,当一个访存指令发出一个内存地址时, CPU按照下面过程实现从指令中的32位逻辑地址到32位线性地址,再到物理地址的转换:
1、首先根据指令的性质来肯定该使用哪个段寄存器,例如操做指令中的地址在代码段CS里,而数据指令中的地址在数据段DS里。这一点与实地址模式相同。
2、根据段寄存器里的内容,找到相应的“段描述符”结构。
3、而后,从“段描述符”里获得的才是段基址。
4、将指令中的地址做为偏移量,而后和段描述符结构中规定的段长度进行比较,看齐是否越界。
5、根据指令的性质和段描述符中的访问权限来肯定当前指令操做是否越权。
6、最后才将指令中的地址做为偏移量,与段基址相加获得线性地址,或者叫虚拟地址。
7、最后根据线性地址算出实际的物理地址。
因此,实模式就是80186及其以前的CPU只能寻址1MB物理地址空间,且寻到的就是实实在在的物理地址的模式,用户程序想干啥干啥,没法无天;而保护模式,就是说用户成的程序,某些地址你是不能访问的,或者说是有限制性的访问,且你访问到的地址再也不是物理地址了,而是一个虚拟的地址。这个虚拟地址要通过一系列算法处理,最终映射到实际物理地址单元里去。
如今运行在X86CPU上的主流操做系统,如Linux,FreeBSD,Windows95之后的版本以及OS/2等都是工做在保护模式下。通常状况下,处理器只有在上电启动,引导阶段,初始化系统时才会进入实模式,当实模式阶段的任务完成后,它就切换到了保护模式。当切换到保护模式后就很难再回到实模式了,几乎不可能。(注意个人用词)
未完,待续…