为什么要有虚拟地址映射???
在实模式下,没有权限的管理,任何程序可以任意访问物理内存,无保护措施,实模式下数据的增删改查是不安全的;为了防止恶意的程序修改段寄存器或IP寄存器的值等,导致不能访问正确的物理地址,我们应该进行权限管理,那么虚拟内存就是基于这样的原因,出现了;虚拟内存的三个主要的作用:<1>虚拟内存是一种内存的管理机制;<2>它为每个进程提供了一致的4G虚拟地址空间;<3>保证了每个进程的地址空间是隔离的,即保护了每个进程的地址空间不被其它进程破坏;那么虚拟地址的映射就是虚拟内存机制中的一种管理方案;
准备工作
IA-32中总共有10个32位,6个16位的寄存器;
分段:将代码、数据、栈分开
分页:用来隔离多个任务
实模式:
Inter8086是由inter1978年所设计的16位微处理芯片,在8086的时候,还没有操作系统的概念;它的数据总线为16位,地址总线为20位;16位的时候,给CPU上放了四个寄存器:CS:代码段的起始地址、DS:数据段的起始地址、SS:栈的起始地址、IP:在某个内存段上的偏移量;对于物理内存,把它划分为一段一段的,每个段的起始地址都是 16的倍数,所以每个段大小可表示的范围为16~2^16(64K);
那么问题来了,16位的数据总线,20位的地址总线,那么16位的寄存器怎么保存20位的地址呢???
因为内存段的起始地址是16的倍数,所以低4位都是0,20位的地址低四位都是0,那么只需要把高16位保存起来,刚好寄存器也是16位;
如果现在我们需要对一个静态变量的地址进行映射,那么它放在数据段,那么接下来,需要找到数据段的起始地址,在DS中保存着,又因为DS中保存的是地址的高16位,因此,需要将得到的地址,左移4位,这个时候,我们得到了数据段的起始地址,即段基址;然后,IP寄存器中保存的是当前内存段的偏移量,只需要将刚才的段基址加上偏移量,就直接得到了数据的地址,即物理地址;详细过程如下图所示:
通过以上得知了实模式下的分段地址映射,我们可以看到,这种模式下的地址访问是及其不安全的,没有权限的控制,可以通过驱动程序随意改变段寄存器中的地址,任何程序可以任意访问物理内存,无保护措施,这样对于物理内存的访问效率就是低下的,所以,后来出现了操作系统,操作系统对于内存的管理提出了页式管理,即对于内存的访问模式为保护模式,接下来,让我们进入Inter 80386的地址映射。
80386是和8086属于同一个体系,所以只能向前兼容,所以又新增加了两个寄存器:GDTR,LDTR;
为了防止实模式下对于物理内存的随意访问,我们需要对于把内个段控制起来,需要把每个段的内存的起始地址、内存段大小、内存段的访问权限等信息都保存起来,GDTR保存的是GDT(全局的段描述表)的地址,这张全局的段描述表是放在内存上的,GDT其实可以看做是一个数组,数组中的每个元素就是保存着:内存段的起始地址、内存段大小、内存段的访问权限等信息,每一项是8个字节,我们称每一项为段描述符表项;内存段的起始地址保存在了段描述符表项中,那么CS、DS、SS、ES寄存器就闲下来了,由于这几个寄存器是在8086-16位CPU中添加的,所以我们在80386-32CPU中不能对于这几个寄存器进行修改大小,只能保留;刚才说,内存段的信息都保存在段描述符表中了,那么访问这个数组,我们缺少了索引,刚好上面的四个寄存器闲着,所以在80386-32CPU中,段寄存器是用来保存GDT表的下标的;下图,展现了段寄存器的格式:
接下来,进入保护模式的地址映射:
以映射数据段的地址为例!!!
1、分段
在寄存器GDTR寄存器中找到GDT的地址,取数据段寄存器中的高13位,作为访问GDT的下标,在GDT中的段描述符表项中取出段基址,加上IP寄存器中的逻辑地址(将IP寄存器中的逻辑地址和段描述符表项中的段大小进行比较判断是否越界),最终得到线性地址(虚拟地址);在寄存器中的CR0的最高位的PG位,进行判断,如果为0,表示未开启分页机制,如果为1,表示开启了分页机制;
2、分页
通过上面的分段,我们可以得到线性地址,又通过判断CR0中的最高位PG位,来判断是否开启分页机制。如果为1,表示开启了,如果为0,表示未开启;现在假设开启了分页机制;我们将得到的线性地址,总共32位,分为10、10、12。高10位表示页目录的下标,次高10位表示页表的下标,低12位表示物理页面的偏移量。
在CR3寄存器中找到页目录的起始地址,将线性地址的高10位作为页目录的起始地址,找到某一项页目录项,因为页面都是以页面(4K)对齐的,所以32位的地址中,低12位都为0,所以,在页目录项中的高20位保存了页表的地址,低12位保存了页表对应的权限;在页目录项中取出高20位,向左移12位,得到页表的物理地址,又通过线性地址的次高10位,作为页表的下标,找到对应的页表项,又在页表的高20位找到物理页面的框号,作为管理物理页面的数组的下标,当找到物理页面以后,线性地址的低12位为物理页面的偏移量,找到真正的物理地址;
补充:
1、LDTR和CS、DS、SS寄存器的意义是一样的,在LDTR中取出在GDT中的下标,然后在GDT中找到LDT所在的地址,访问LDT的详细信息;
2、CPU内总线发出的地址是逻辑地址;
3、交换分区:磁盘空间,根据LRU最近最久未使用算法,脏页才会被交换,在PTE中有1位为dirty位,这个位表示是否为脏页;
4、物理内存的分配是延迟到不能再延迟的时候,才分配;当程序第一次运行的时候,程序跑到页目录,发现页目录项是空的,产生缺页异常(do_page_fault),有MMU产生的,由异常处理程序分配页表,重启地址映射的过程,当又走到页表项的时候,发现页表项是空的,又会发生缺页异常,知道分配了物理页面。当内存不够用的时候,根据特定的算法选择一个牺牲的页面,把这个页面放在交换分区上,同时把当前腾出来的物理页面的地址放在页表项中;如果够用的话,直接分配咯;这样一趟的映射看起来是很麻烦的,其实,一般的程序运行都有局部性原理,大部分是停留在同一页面上的,所以发生上面的映射过程的概率是很小的;
5、缺页中断:包括软件中断和硬件中断;如果进程发生中断,需要保存进程的现场信息,保存完毕,再去执行中断处理程序,执行完中断处理程序以后,程序会紧接着下一行指令开始执行;
6、缺页异常:异常的发生不是故意的,异常是由于代码参数不合法等情况发生的,异常处理程序执行完以后,重新执行发生异常的指令。