虚拟地址向物理地址的转换

每个程序员都梦想拥有这样的内存:它是私有的、容量无限大的、速度无限快的,并且是永久性的存储器(即断电时不会丢失数据)。计算机中的存储层次结构都是在考虑性价比的前提条件下尽量满足程序员的这一需求,其中虚拟地址的产生在其中起到了至关重要的作用:它允许程序员不用考虑物理内存的使用情况而任意使用整个内存空间(CPU地址总线决定)。

总体概述

虚拟地址-物理地址概述
(这里以虚拟cache为例)

  1. CPU核发出VA请求读数据,TLB(translation lookaside buffer)接收到该地址,那为什么是TLB先接收到该地址呢?因为TLB是MMU中的一块高速缓存(也是一种cache,是CPU核和物理内存之间的cache),它缓存最近查找过的VA对应的页表项,如果TLB里缓存了当前VA的页表项就不必做translation table
    walk了,否则就去物理内存中读出页表项保存在TLB中,TLB缓存可以减少访问物理内存的次数。
  2. 页表项中不仅保存着物理页面的基地址,还保存着权限和是否允许cache的标志。MMU首先检查权限位,如果没有访问权限,就引发一个异常给CPU核。然后检查是否允许cache,如果允许cache就启动cache和CPU核互操作。
  3. 如果不允许cache,那直接发出PA从物理内存中读取数据到CPU核。
  4. 如果允许cache,则以VA为索引到cache中查找是否缓存了要读取的数据,如果cache中已经缓存了该数据(称为cache hit)则直接返回给CPU核,如果cache中没有缓存该数据(称为cache miss),则发出PA从物理内存中读取数据并缓存到cache中,同时返回给CPU核。但是cache并不是只去CPU核所需要的数据,而是把相邻的数据都取上来缓存,这称为一个cache line。ARM920T的cache line是32个字节,例如CPU核要读取地址0x300001340x3000137的4个字节数据,cache会把地址0x300001200x3000137(对齐到32字节地址边界)的32字节都取上来缓存。

下面我们来详细看下这个过程

TLB对VA的处理

VA到TLB

TLB页表项的内容如下图:

Cacheable[1] AP[3] Valid[1] Dirty[1] Use[1] TAG(VPN) PFN
1 1 0 1
  1. TLB从地址线获得VA后,首先根据地址中的TAG(VPN)(后面介绍地址与页表项的对应关系)在缓冲中查找对应的页表项。
    1. 如果存在,则查看权限(AP)位是否正确。
      1. 如果权限正确,则设置Use位(Dirty/valid等),并查看是否允许缓存。
        1. 如果允许缓存,则将PFN发送给cache,并向Cache line fetch电路发出物理地址[TLB hit]。
        2. 如果不允许,则直接根据物理地址从主存中取出数据交给CPU(怎么交?主存和CPU有直接通信?)
      2. 如果权限不正确(该TLB&mem页表项无效),则向CPU发出缺页进行软件处理(下次访问时更新正确的TLB页表项)[线路3]
        (此时内存中的页表项已经更改,如何同步到TLB中?)
    2. 如果不存在,则通知Translation table walk电路从内存页表中获取该页表项,并根据置换算法放入相应的单元,此过程很快,然后进入页表项存在于TLB的情况。
      Translation table walk电路根据寄存器PTR的值(指向内存中页表的基地址,其为物理地址)和虚拟地址找到对应的页表项。

注意1:Cacheable的意思是:是否允许在cache中缓存该页表项对应的数据块,而不是在cache中是否已经缓存了该数据块。
注意2:TLB是页表的cache,其记录的是内存页-页框的信息,和cache并没有直接关系,所以可以直接将TLB中的页表项当做内存中的页表项来理解。
注意3:TLB页表项可以被写入。
注意4:此图中的cache是物理cache。
注意5:从线路1/2可以看出,虚拟地址VA的大小需要在1/2地址线的限制范围内,比如1/2线路的地址线是32位,则最大可寻址虚拟地址空间位2^32=4G,换句话说就是:应用程序的最大访问地址不能超过4G,当然虚拟地址可寻址空间还和页表有关;

cache对VA的处理

VA到cache
细分虚拟地址

上面两张图结合在一起看。

cache从地址线获得VA后只使用其offset[0:11]位(称为:cache index),用其在cache中进行查找数据,可以看到offset为12位,可查找地址最大为4K,而存在n路组关联则同一地址可以访问到n个数据,然后再根据TLB传送过来的PFN[线路5]进行对比决定是哪个数据命中。

如果没有数据hit,则cache会自动向下一级存储单元请求数据,并根据TLB所给的物理地址获得数据。

SOC内部的问题

  1. CPU讲VA同时发送到TLB和cache上,但是如果cache中找到了,由于没有通知TLB,这样TLB什么时候算结束呢?
    答:TLB只负责将虚拟地址转化位物理地址,不管是否访问数据,只要访问了地址,就需要把改地址对应的页表项缓存在TLB中,同时讲物理地址发送到Cache line fetch电路上,至于是否去根据该地址取数据还要根据cache的需要。
  2. TLB中的页表项和内存中的页表项是如何同步的,其中没有Dirty位决定该页表项是否被写脏过?
    答:TLB中页表项数基本不会超过64个,而且其中的Dirty/Use位经常会变化,所以如果需要空位置就直接替换出去,而不用考虑是否可以直接丢弃该页表项问题。

cpu与页表的交互

上面的问题解决了,我们就可以当做CPU和主存直接交互了,进入到页表的具体情况。
下图即为TLB转换的简化版本。
va_pa.png

首先CPU从指令中获取到虚拟地址,然后该虚拟地址交给MMU,通过MMU中的TLB索引物理地址,如果TLB不存在该虚拟地址的entry,则到物理内存中对应的页表中查找,如果页表项存在,则可以根据页表项找到物理地址,并更新TLB,接着根据指令类型和页表项位标识读写cache/mem,如果页表项不存在就会产生缺页异常。

页表项结构

page_table.png

可以看到虚拟地址包括:虚拟叶号(VPN)和偏移地址
而虚拟叶号中包括:叶项有效位,访问控制位(RW)和物理帧号(PFN)
重新划分后可以得到物理地址:物理帧号和偏移地址

注意:这里和TLB中虽然表达的内容是一样的,但每一个entry的表达方式是不同的,由于TLB空间较小,不能以虚拟地址进行寻找,所以必须在entry中保存虚拟页号(VPN)这个参数,而内存中的页表可以以虚拟地址进行寻找找到对应的物理地址,所以entry就不需要再包含虚拟页号(VPN)了,其他的位含义都一样。

缺页异常

page_fault.png

MMU根据虚拟地址查找页表项,如果对应页表项无效(有效位标记??权限问题呢?他们有什么区别?)则就会产生缺页异常,此时MMU会从内存匿名池中获取一帧内存,更新页表项信息,然后从Disk中搬移数据填充。如果内存已全部使用完,则需要根据替换策略替换出一帧内存,并跟新页表项。 

不同的层次结构

virtual_physical_cache.png
物理cache:因为采用TLB管理,有很好的替换策略,对于命中率不高、需要频繁切换的应用效率比较高。
虚拟cache:cache命中率比较高(单线程优)时采用虚拟cache,因为地址翻译延迟很高,不能充分发挥cache的优势。
虚拟-物理cache:减少了关键路径,少见。

补充

OS产生之前我们基本采用手动分配内存,而对于未知的应用又没法定义应用所使用的内存的范围,OS之后的虚存可以对内存进行妥善的管理,使通用性大大增强,并可以保证多个任务顺利安全的进行。(域的管理用的比较少)
对于每个应用都存在0~3G的用户空间内存,应用可以采用连续的某一段虚拟地址进行访存,其中内存的分配:代码段+常量数据段+全局数据段,按顺序一层层生长,堆数据也同样往上长,栈数据往下压。
随应用进程产生虚拟地址从匿名池(空闲内存管理池)中申请物理内存与虚拟地址关联,应用进程关闭,物理内存得到释放(取消关联)。

疑问点

  1. 我们常说的用户空间(03G)和内核空间(34G)指的是虚拟地址空间还是物理地址空间?

  2. 如果“疑问点1”中的空间是指物理地址空间,是不是可以说程序员写程序时可以使用内核空间?

注意

  1. 页表存在意义:
    1. 解决单级页表必须连续存储的问题:因为页表是根据偏移量来访问的,两个虚拟地址连续的页表必须物理地址也连续才能通过偏移量来访问;
    2. 解决所有页表占用空间过大的问题:上面说连续所以空间是不可能缩小到的,而如果采用多级页表,如果某个页表暂时(或者永远)用不到就不会为这个页表分配真正的存储空间,可能一个程序也就占用3个二级页表而已(总过:目录4K+3个二级页表3*4K = 16K),这样可以大大缩小页表所占用的空间。

参考文献

处理器访问内存时,CPU核、cache、MMU如何协同工作
Linux用户空间与内核空间数据传递

Brick wechat
扫一扫,用手机看更方便(^ ◕ᴥ◕ ^)