找回密码
 立即注册
Qt开源社区 门户 查看内容

linux内存管理(四)页表机制引入

2019-9-5 15:55| 发布者: admin| 查看: 977| 评论: 0

摘要: 前面我们介绍了内存管理的三级架构:内存节点-内存区-页。今天我们就介绍由页引入的页表机制。linux采用了一种同时适用于32位和64位系统的普通分页模型。对于像32位arm系统来说两级页表已经足够,但64位系统需要更多 ...
前面我们介绍了内存管理的三级架构:内存节点-内存区-页。今天我们就介绍由页引入的页表机制。

linux采用了一种同时适用于32位和64位系统的普通分页模型。对于像32位arm系统来说两级页表已经足够,但64位系统需要更多数量的分页级别。2.6.10版本以前,linux采用三级分页的模型,从2.6.11版本开始采用4级分页模型。4级页表如下:

页全局目录(pgd)

页上级目录(pud)

页中间目录(pmd)

页表(pte):页内偏移



图 四级页表映射图

说明:控制寄存器cr3中存放了页目录PGD的物理地址,通过cr3寄存器可以找到页目录,不同的进程有不同的页目录地址。进程切换时,操作系统负责把页目录地址装入CR3寄存器。备注:这是在32位pc机上的情况,arm没有cr3寄存器,放在了另外的地方,后面会介绍。

我们来解释下四级页表的映射过程。

一个线性地址被划分成了五部分,通过某种方式得到pgd的地址,也就是顶层页目录的地址,顶层页目录顺序存放着许多页目录项,以线性地址的第一部分作为索引找到对应的顶层页目录项,所谓的页目录项是指下一级页目录的地址,一个页目录项32位。通过顶层页目录项我们就可以找到二级页目录的地址,每一个顶层页目录项都对应着一个二级页目录。同理,以线性地址的第二部分为索引可以得到二级页目录项,依次类推,得到最后一级,我们把它叫做页表项,页表项存放着线性地址对应的物理页的地址,而线性地址的最后一部分,12位,表示页内偏移,通过这个页内偏移,我们就能找到页内的任一字节。

这个地址翻译过程完全是由硬件完成的。

到这里,我们抛出一个问题:为何要采用多级页表,一级页表不行吗?直接20位表示一个页表,12位表示页内偏移,一共可以存放1M个页表项,一个页表项可以表示4K,也就是一共映射了4G空间。

首先我们来计算下各级页表需要占用的空间。

一级页表:

对于4G的空间,4K页大小,那需要2^20 == 1M个页表项(无论用不用都需要,因为并不知道哪个地址是否会被访问),1M * 4Byte == 4M。

也就是说每个进程都需要4M的内存(每个进程最小需要4M的内存用于存放页表项),为啥是每个进程?因为每个进程的虚拟地址映射

二级页表:

对于4G的空间,4K页大小,那么需要一个页目录(1024项*4字节=4K)+ 4K*2^10个页表项=4M+4K的空间。

假设进程只需小于4M的内存空间,那么只需要一个页目录(1024项*4字节=4K)+ 4K*1个页表=4K+4K=8K。

总结下一级页表的两个问题

1.页表在内存分配时必须连续,否则无法查表。

2.每个进程都必须实实在在占用4M的空间用于页表,必须全部分配,进程才可以使用。

二级页表的优势

1.允许页表被分散在内存的各个页面中,不需要连续的4M内存块,只需要连续的4K内存块就可以了,因为只要一个页目录项的所有页表项连续即可;

2.并不需要为不存在的或线性地址空间未使用部分分配二级页表,用到时再分配页表,这也是二级页表能节省内存的原因;

3.可以在虚拟内存中存放二级页表。



图 二级页表示意图



上面这张图片形象的解释了二级页表相对于1级页表的好处:

(1)一级页表中的一个PTE是空的,其对应的二级页表就不存在了,节约了很大部分内存。 (2)只有一级页表需要总是存储在内存中,其二级页表使用时才会加载进内存,除非频繁使用的二级页表。

那么多级页表有没有坏处呢?当然是有的。不然arm干嘛不用3级、4级甚至n级呢,而要用二级页表呢。

使用一级页表时,读取内存中一页内容需要2次访问内存,第一次是访问页表项得到虚拟地址所对应的物理页的地址,第二次是访问要读取的一页数据。但如果是使用二级页表的话,就需要3次访问内存了,第一次访问页目录项得到页表项的地址,第二次访问页表项得到页地址,第三次访问要读取的一页数据。访存次数的增加也就意味着访问数据所花费的总时间增加。

人类是聪明的,针对多级页表这个缺点,又发明了缓存机制。即将最近访问过的或者频繁访问的页的页表项放于缓存之中,下次先去那里看,如果有直接去缓存拿。也就是下面的图:



图 引入TLB的地址转换示意图

补充介绍TLB

CPU的Memory management unit(MMU)cache最近使用的页面映射。我们称之为translation lookaside buffer(TLB)。TLB是一个组相连的cache。当一个虚拟地址需要转换成物理地址时,首先搜索TLB。如果发现了匹配(TLB命中),那么直接返回物理地址并访问。然而,如果没有匹配项(TLB miss),那么就要从页表中查找匹配项,如果存在也要把结果写回 TLB。

 CPU访问内存时的硬件操作顺序:

1 CPU内核(图中的ARM)发出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内核要读取地址0x30000134~0x3000137的4个字节数据,cache会把地址0x30000120~0x3000137(对齐到32字节地址边界)的32字节都取上来缓存。

牛客网居然考到了多级页表相关的知识点:



图 牛客相关题目


----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:嵌入式开发者,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了