内存管理<原理篇>(五、页表和快表)

5.1 页表

5.1.1 页表介绍

虽然我们上一篇也简单提过页表,感觉页表也很简单,就是把内存分页之后,在进程中逻辑内存是连续的,存到物理内存中是离散的,所以一个表来记录这个关系。

这个表的结构为:

页号页框号保护
05R
11R/W
23R/W
36R

通过逻辑地址的页号,查找到对应物理地址的页框号。

这就是页表的作用。

通常会在系统中设置一个页表寄存器(PTR),存放页表在内存中的起始地址F和页表长度M。进程未执行时,页表的起始地址和页表长度放在进程控制块(PCB)中,当进程被调度时,操作系统内存会把它们存放到页表寄存中。

5.1.2 页表项

是不是觉得页表就这么简单??

通过我需要用一篇文章来介绍就明白,这个页表不简单了,那不简单在哪里???

我们是不是忘记计算页表项的个数了?

按照我们现代操作系统分页的大小一般是4K,然后现在的操作系统内存是4G,(当然目前内存肯定是16G,32G了,不过我们就按课本上的,按4G来计算。)

然后通过计算,一共有多少个页表项:4G/4K = 1M = 1,048,576个。

wakao,1百万个页表项,如果是16G,那就是4百万个页表项。

如果是遍历查找,这需要查到明年(这是夸张的手法,哈哈哈)。

那一个页表项有多大?

页表项的作用就是为了能查到这1百万个页表项,所以只要范围能在1百万以内就可以了。

上面我们计算的页表项个数为:220 = 需要20个二进制位来这么多个内存块号 = 需要3个字节就可以了

为了内存对齐访问,一般会扩展到4个字节,也就是这个页表项 大小为:220 * 4 = 4M字节存储。

每个进程需要4M来存储页表项,如果很多个进程呢??那不得上天了。

正因为页表项很大,不方便查找和储存,所以才需要开始研究页表的结构。

(这里就有人抬杠了,说不是挺方便查找的,用数组的方式,但是用了数组的方式容易查找,就不容易存储啊,又有人说用链表结构来存储,链表结构是容易存储,但是不容易查找,所以我们还是到后面的章节来学习一波,下一节就见分晓)

5.2 页表结构

哈工大老师说的有道理,正因为有多个解的时候,才需要算法,因为页表很大,不要存储和查询,才需要研究一下页表存储的结构,和查询的算法。下面我们就来学习学习。

5.2.1 多级页表

通过上面的分析,我们知道了页表会很大,达到1百万个条目,如果一个条目为4字节,大小达到4M。那如果按照数组的方式储存,我们需要连续开辟4M的大空间,我们前面学习了分页,就是为了把连续大空间给拆小,现在又这样,明显不对。

那我们是不是可以借助分页的思想,进行再分页,没错多级页表就是这种思想。

我们现在就按二级页表来分析一下,虽然linux使用的是三级页表,不过我们先学习二级页表。

我们一个页为4K,4K=212,所以我们页偏移只需要12位即可。

我们从上面分析到4G内存,一个页4K,我们需要220个表。我们现在分为二级页表,也就是第一次为210,第二级也为210

在这里插入图片描述

这是我画的一个二级页表,画的比较大,也是因为要符合比例吧。

最左边的是页目录号,也成顶级页表,一共有1024个,每个4byte,刚好是4k,一个页,就是这么凑巧,其实并不是凑巧,就是这么设计的。

二级页表,也是一共有1024个,每个为4byte,存储的是4M的偏移,4M是怎么来的??

因为页大小为4k,个数刚好是1024个,相乘就刚好4M,一个二级页表管理4M,那一共有1024个,管理的就是4G,这就是二级页表的来历。

举个例子,如果我们要找到逻辑地址为0x00403004的物理地址是多少:

我们转换成二进制:01,0000000011,000000000100

对应上面的多级页表:PT1=1,PT2=3,Offset=4。

我们就按上面的图来看吧,PT1=1就相当于我们找到了页号为1的页面,然后继续在这个页面中找到3的位置,上面我没画,就看是3吧。那相当于我们找到的页框号为1027个,计算出地址为:(1024+3)*4k = 4,206,592 然后在加上偏移就等于 4,206,596。

为啥这个逻辑地址跟物理地址一样呢??其实是我们PT1和PT2的索引是一样,所以求出来的结果一样,其实实际上,这两个应该是没有关系的。

如果该页面不在内存中的,页表项中其实中一位表示"在不在内存中"的意思(页表项位还是很多剩余的),如果不在,这个位为0。然后会引发缺页中断。如果在的话,就直接访问物理地址了。

实际上,我们的进程虽然管理内存有4G,但是我们程序是分段的,代码段一般都是在最低(0~4M),数据段会紧跟着代码段(4M-8M),然后就是最顶端的堆栈段了4M,按照这个二级页表,我们这个进程运行的话,主需要4*4K=16K的页表内存就够了,最后一个是页目录表。如果是单级页表就需要4M,这差别很大了。

5.2.2 哈希页表

处理大于32位地址空间的常用方法是使用哈希页表(好像我没见过,哈哈哈),采用虚拟页码作为哈希值(应该一级的页码)。

哈希表的每一个条目都包括一个链表,处理哈希碰撞的。

该算法工作如下:虚拟地址的虚拟页码到哈希表中查找,查找的话,判断是否存在链表,如果有链表就需要一个链表一个链表的查找,看看是否陪匹配虚拟页码,如果匹配就取出响应的页框码,通过这页框码计算出物理地址。

这个就不画图的,就是一个哈希的使用,比较简单。

5.2.3 倒置页表

我们上面学习的多级页表,是从虚拟地址出发,然后采用分级分页,最后才映射到物理地址。如果虚拟地址过大于物理地址,就会导致页表项很多,然后其实映射到物理页还是一样的,所以我们把这种关系进行倒置,这就是倒置页表

倒置页表,是以物理内存为中心,把物理内存进行划分成页,如果一个4G的内存,4K的页,就分为220个页表项,注意:这里是整个系统有220个页表项,不像虚拟内存,有n个进程,就有n*220个页表项。

如果这样存储的话,那虚拟地址怎么映射到物理地址???

这个虚拟地址的定义也跟我们之前不一样了,每个虚拟地址为一个三元组:<进程id, 页码, 偏移>

而每个导致页表的条目为二元组<进程id, 页码>。

当发生内存引用时,由<进程id, 页码>组成的虚拟地址被提交到内存子系统,然后搜索整个倒置页表来寻找匹配,如果匹配,通过这个条目i(因为物理地址是有序),在加上偏移即可得到物理地址。

在这里插入图片描述

这个图就是工作的过程,通过条目i*4K+offset来计算物理地址,然后找到对应的内存。

这个倒置页表有啥缺点呢?

很明显嘛,搜索过程就是一个缺点,一遍一遍的搜索全表肯定慢,要想提高这个速度,有两种方法。

第一种:可以使用一个哈希表,就是上面所描述的,直接用哈希表找到了条目i。

第二种:就是后面要介绍的TLB快表。

5.3 快表

因为有多级页表的存在,如果是二级页表,我们访问一次物理地址,需要3次内存访问,第一次访问页目录表,第二次通过页目录表,访问页表,第三次才访问物理地址,按这种访问速度,确实比之前的慢了很多。

5.3.1 局限性原理

看了王道论坛的操作系统,在3.1.8具有快表的地址变换机构一节中,提到了局限性原理。我看着感觉挺不错的。

时间局限性:如果执行了程序中某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问。(程序中存在大量的循环)

空间局限性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。(因为很多程序在内存中都是连续存放的)

5.3.2 快表机制

通过上面的局限性分析,我们就想到不用每次都去访问多级页表,而是可以在添加一个小的表,这个表就是转换表缓冲区(Translation Look-aside Buffer, TLB)或快表

现代的TLB查找硬件是指令流水线的一部分,基本上不添加任何性能负担。为了能够在单步的流水线中执行搜索,TLB不应大,通常它的大小在32~1024之间。有些CPU采用分开的指令和数据地址的TLB。

在这里插入图片描述

TLB与页表一起使用的方法如下:当CPU产生一个逻辑地址后,它的页码就会发送到TLB。如果TLB中有这个页码的缓存,那就可以直接取出帧码,然后就根据偏移,就可以找到了物理地址。因为TLB是在寄存中,访问速度很快,这样获取几乎没有浪费时间。

如果页码不在TLB中(称为TLB未命中),这时候操作系统一般会出现缺页中断,现在就需要访问页表,通过页码找到对应的帧码,然后在访问物理内存。另外,会将页码和帧码添加到TLB中,这样下次再访问的时候就可以快速找到。页表存储在内存中,需要找到内存的时候,相对会比TLB慢很多,如果是多级页表,速度会更慢。

如果TLB满的话,我们就需要使用淘汰机制,淘汰机制都是相同的,可以使用LRU或者随机替换啥的。

上面画的图就是这样。

5.3.3 访问速率

上面我们也提到访问TLB的速度比访问页表的速度快,那我们现在就来看看加上了快表之后速率怎么算?

我们使用TLB,比较感兴趣的就是TLB的命中率,根据5.3.1的局限性原理上面,我们知道程序运行时,访问的内存页码都是附近的,所以TLB的命中率很高,起码能到达90%。

我们就按90%的命中率来计算,访问TLB的时间为20ns,访问内存的时间为100ns。

按照公式:
有 效 访 问 时 间 = 命 中 率 ∗ ( T L B + 内 存 访 问 时 间 ) + ( 1 − 命 中 率 ) ∗ ( T L B + 2 ∗ 内 存 访 问 时 间 ) 有 效 访 问 时 间 = 命 中 率 ∗ ( T L B + 内 存 访 问 时 间 ) + ( 1 − 命 中 率 ) ∗ ( 2 ∗ 内 存 访 问 时 间 ) / / 直 接 同 时 访 问 有效访问时间 = 命中率 * (TLB + 内存访问时间) + (1-命中率)*(TLB + 2*内存访问时间) 有效访问时间 = 命中率 * (TLB + 内存访问时间) + (1-命中率)*(2*内存访问时间) // 直接同时访问 访=(TLB+访)+(1)(TLB+2访)访=(TLB+访)+(1)(2访)//访
这个公式是快报和页表不能同时查询,如果可以同时查询,后面访问内存的时间可以去掉TLB。

有效访问时间 = 0.9 * 120ns + 0.1 * 220ns = 130ns

如果没有快表,那时间为 2 * 100ns = 200ns。(这个是一级页表的情况)

所以快表的存在也提高了CPU访问物理内存的效率。

5.4 总结

这一篇,介绍了页表和多级页表还有快表,再加上前面的分段,现在操作系统的内存管理已经出来了,下一篇就开始介绍段页结合的实际内存管理了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值