虚拟内存
- 1. 虚拟内存的起因(缓解内存不足)
- 2. 覆盖技术(80年代90年代初)
- 3. 交换技术
- 覆盖与交换
- 4. 虚拟内存管理技术(虚存技术)
- 程序的局部性原理
- 基本概念
- 基本特征
- 虚拟页式内存管理
- 介绍
- 页表表项
- 缺页情况
- 缺页处理
- 后备存储
- 虚拟内存性能
- 5. 页面置换算法
- 5.1 功能和目标
- 5.2 最优页面置换算法
- 5.3 先进先出算法(FIFO)
- 5.4 最近最久未使用算法(Least Recently Used,LRU)
- 5.5 时钟页面置换算法
- 5.6 二次机会法
- 5.7 最不常用法(Least Frequently Used,LFU)
- 5.8 Belady现象、LRU、FIFO、Clock的比较
- Belady现象
- LRU、FIFO、Clock的比较
- 6 全局页面置换算法
- 6.1 局部页替换算法的问题、工作集模型
- 工作集模型
- 1 工作集(working-set)
- 2 常驻集
- 6.2 两个全局置换算法
- 6.2.1 工作集页置换算法
- 6.2.2 缺页率页面置换算法
- 6.2.3 抖动问题
1. 虚拟内存的起因(缓解内存不足)
内存越来越不够用,程序规模的增长速度远大于存储器容量的增长速度
registers(寄存器):把更小更快的存储器放在离CPU近的地方,使CPU更快访问到
Cache:由于内存相对寄存器速度较慢,所以在中间加一个cache,缓存内存数据,使得CPU取数据尽量从cache取,而不需要每次都访问主存,保证速度快
把寄存器和Cache容量做大不太切实际
内存现在越来越大,然而软件也越来越大
硬盘速度太慢,不能把程序放在这执行,考虑将硬盘容量用上。希望操作系统能帮助程序员利用这些硬盘空间,从而得到一个更大更快的虚拟空间。为了有效管理物理内存,出席拿了分页分段机制,希望能在分页分段之上解决容量不够的问题
现有的物理内存掉电数据就丢失了
希望充分的,利用硬盘,把不常用的代码数据放到硬盘,常用的代码数据放到内存,虚拟出大内存的感觉,需要硬件、CPU、MMU、操作系统的内核协同在一起,
overlay:最开始,内存很小,需要程序员手动写代码,把一些常用的数据保存到内存,不常用的放到硬盘
swapping:后来,由os把不用的数据导出到硬盘,开销大,要把一整个程序导出和导入
虚拟内存技术:把程序中的一部分数据导出,基于分段分页+os管理实现虚存
2. 覆盖技术(80年代90年代初)
操作系统能力很弱,代表是dos系统,硬件只有640k的内存,小内存却要运行大程序
程序员自己实现数据的换入换出
原理:将程序根据执行逻辑进行分类,划分为若干个区间(相对独立的模块)。不能同时运行的模块设置其为共享空间,按时间模式依次执行(分时),要设置常驻内存空间,负责管理何时将相应的函数数据调入内存调出内存,不常用的数据在其他程序执行时要放到外存,用到的时候调入内存
A调用B、调用C
- A先调用B,所以B从硬盘调入内存,
- 此时,A又调用C,则把B使用的空间释放掉,把C从硬盘调入内存;
- C调用E,则把E从硬盘调入内存,C调用E的时候B和F不会被执行,因为是串行,所以B和F不会被放到内存中
- E执行完后,C会调用F,所以会把E占用的内存空间释放掉,把F从硬盘导到内存中去,使得F可以正常执行
粒度以程序的逻辑调用关系来实现
逻辑调用关系不止一种覆盖方法:
缺点(开销):
- 程序员设计的开销(把大的程序分成几个小的功能模块,如何找到最佳的覆盖方法使得占用空间最小)
- 数据的换入换出(时间换空间)
3. 交换技术
linux使用的一种方法
操作系统帮助完成,当多个程序再跑的时候,使用操作系统来管理这些程序合适换入换出内存
不用的程序且内存不够就把它换出去(写入外存),用的时候再换回来
粒度:一个程序
问题:
- 什么时候交换?内存不够时才作此操作
- 交换区大小?要够那个程序运行
- 程序换出后再换入位置不一样了?(重定位)若换入后还是以前的位置,继续执行;若不是原来的位置了,就采用动态地址映射(页表,动态表变化的,虚地址与实地址的映射关系是动态变化的)
覆盖与交换
覆盖 | 交换 |
覆盖发生在一个程序里,使一个程序中的多个没有调用关系的模块共享一个内存区域 | 交换是在程序间 |
覆盖由程序员手动指定逻辑关系 | 交换由操作系统内部完成 |
粒度是模块 | 粒度是程序,开销比较大 |
4. 虚拟内存管理技术(虚存技术)
为解决覆盖和交换存在的问题,提出了虚存技术
目标:集2个优点于一身
- 和覆盖一样,内存中存的不是整个程序,但此操作由os来完成
- 和交换一样,对其进行换入换入换出,但粒度小
对于P3,不是所有的数据都放在了内存
程序的局部性原理
指程序在执行时呈现出局部性原理,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应的,执行所访问的存储空间也局限于某个内存区域。
局部性原理又表现为:时间局部性和空间局部性。
- 时间局部原理是指如果程序中的某条指令一旦被执行,则不久后该指令可能再次被执行;如果某数据被访问,则不久后该数据可能再次被访问。
- 空间局部性是指一旦程序访问了某个存储单元,则不久后,其附近的存储单元也将被访问。
(更详细的见https://www.jianshu.com/p/5c9b28c95c64)
如果能实现程序的局部性原理,就可以高效的完成虚存技术
一个int整型在32位os占4ByteA[1024][1024]
占用102410244=4M
但物理内存只有4K
数组在内存中是按行存储的
一页为4K,而a0,0到a0,1023占210 *4=4K
一页正好是数组的一行
解法1将先将a0,0-a0,1023调入,访问完a0,0后,访问a1,0,需要再次调入a1,0-a1,1023,产生缺页,开销很大
基本概念
把程序的一小部分放入内存中,如果要访问的指令或数据不在内存中,就把它从硬盘中调入,如果内存足够大,直接调入,如果内存不够大,则由os来选择一个以后不会或较少用到的部分调出内存
基本特征
- 空间大:物理内存和硬盘的一部分相加得虚拟空间
- 部分交换:换出的是段或页,不是换整个程序,分段和分页是由操作系统管理的
- 不连续:物理内存分配得不连续,虚拟地址空间使用的不连续(不连续体现在本来所有的都应该连续的放在虚拟内存空间中,但是某些数据或代码会被换出去,所以会不连续。但操作系统会帮我们弥补好,使得程序能够正常的被访问)
虚拟页式内存管理
介绍
查找页表,如果存在位为0的话,虚拟内存空间对应的物理空间是没有的,就会发生访问异常,这个访问异常就是虚拟内存管理的有效手段
- 大部分采用页式存储管理,还要增加请求调页和页面置换功能
- 基本思路
- 请求调页:只装入部分页,访问到不在内存的页时,请求调页,CPU向操作系统发出缺页异常,根据缺页的信息找出相应的页,调到物理内存中去
- 页面置换:内存不够时调入调出
需要换进来页时,把哪些页换出去对效率很重要
页表表项
为实现调换和置换,增加几个位
- 驻留位:该页在内存还是外存
- 保护位:能否访问该页
- 修改位:该页是否被写过。如果被写过,说明与放在硬盘得数据是不一致的,在做换入换出时,要写回;若没被写过,则直接释放该页
- 访问位:当前页是否被经常访问,若不是经常访问(0),则可以换出
缺页情况
X标识驻留位为0,数字表示驻留位为1
左面是页表
MOV REG,0 //8192
把虚拟的0地址赋给寄存器,页表里0对应的是2,2一个页的大小即24K=2*4096=8192,所以物理地址位8192
MOV REG ,32780 //缺页中断
即页表项第8项 32K-36K(32780/1024=32.011),该项是X,所以是缺页
缺页处理
若CPU执行一个指令,若内存地址无对应的映射关系,则缺页异常,os来处理,看内存中是否有空闲空间,
- 若有,则分配一个物理页帧,转到第4步,把该物理内存以页的单位转入到分配的物理页帧那,然后修改页表,把存在位(驻留位?)置为1,重新执行指令
- 空闲空间没有了,则要采取页置换算法
后备存储
- 数据文件,如一个数组会以文件的形式放在硬盘上的,访问若没有,则从硬盘中把这个数据文件读出来,放到硬盘的文件上叫做后备存储
- 代码,每一条指令也是数据,放在硬盘,把执行代码作为数据读入内存,进一步执行。访问某条指令不存在时,就会从执行程序中把它读到内存来
- 库文件:需要才从硬件读出来
- 程序在运行过程中会产生数据,这些数据占空间且需要换出到硬盘,硬盘直接开辟一个空间(swap file)
以上保证了空间有效性
虚拟内存性能
取决于缺页次数及硬盘读写开销
p——缺页概率
q——对页需要写回的概率
EAT=10(1-p)+5,000,000p(1+q)
(1+q):写回还要占时间
p足够小,则是EAT接近10,若程序具有局部性原理,产生缺页的次数就会很少,则效率很高,则会接近10
5. 页面置换算法
5.1 功能和目标
功能:当缺页中断发生时,需调入新的页面而内存已满时,选择内存当中哪个物理页面被置换
目标:尽可能地减少页面换进换出次数(即缺页中断的次数),因为硬盘读写速度慢,所以要尽量减少次数
常驻内存要进行页面锁定
因为找的时候如果那一个也都不存在了,也就不需要再看偏移量了,所以就只需要考虑页即可
不太可能做到不缺也,只能缺页概率减少
5.2 最优页面置换算法
根据将来这个程序将来会访问哪个页来设计
很难实现
但可以把算法算作最佳的评算标准,越逼近则越好
分配4个物理页帧,当访问第5个页时就需要置换:
- 0时刻时,假设abcd就已经在内存中了,1-4时刻都命中了,不会产生缺页中断
- 5时刻时,需要置换
要选离当前时刻距离最远的来做替换
5.3 先进先出算法(FIFO)
驻留最久的就被换出
使用一个链表,链表头部是驻留时间最久的页面,尾部插入的是驻留时间最短的。新页添加在链表尾部,同时删除头部
缺点:
- 性能差。产生缺页次数比较多
- Belady现象。给的物理页帧,产生缺页次数越多的现象
0时刻,假设安装abcd的顺序,所以5时刻时驻留时间最长的是a
实现简单,但产生的缺页次数较多
5.4 最近最久未使用算法(Least Recently Used,LRU)
选择过去最久未使用的页面,根据过去来推测未来,最优是根据未来推测未来
效果虽好,但开销很大
5.5 时钟页面置换算法
Clock 页面置换算法,LRU的近似,对FIFO的一种改进
访问位,若访问则置1,由硬件完成
把各个页面组织成环形链表(放在物理内存),若访问位为1,则不是个老页,指针下移,若访问位为0,则是老页,就可以把它换了
是否是老页,依据是访问位,定期会把访问位置0
依据used bit(右边数第2位)
当前指向Page 0: 1 1 4
,访问位为1,说明最近被访问过,此时需把访问位置0,再把指针顺时针下移到Page 3:1 1 1
,一直找到访问位为0的,即找到Page1,所以把Page1 的页表项换掉
命中指针不移动
与LRU缺页次数差不多,达不到,但很接近
注意:当访问位为1时,将访问位置为0并下移指针
访问:读和写都是访问
5.6 二次机会法
使用used bit和dirty bit
脏位:写操作则置为1,由硬件设置
若进行读操作,则内存和硬盘的数据一致,无需写回
若进行写操作,则内存和硬盘的数据不一致,需写回
提出二次机会法(Enhanced Clock algorithm),同时利用脏位和访问位来决定那页可以置换,以减少写回
上图右下角为替换规则,如2位均为0,则该页可以替换,如分别为0,1,则把脏位设为0,指针下移
如2位均为1,则变为0,1,指针下移,当指针再次回到该页时,变为0,0(这里体现了2次机会)经常使用的dirty位会更少的换出
带上标的为对其进行写操作
time=5:
- a:1,1->a:0,1
- b:1,1->b:0,1
- c:1,0->c:0,0
- d:1,0->d:0,0
- a:0,1->a:0,0
- b:0,1->b:0,0
- c:0,0->e:1,0(替换)
time=9:
指针指向00 d,直接替换为10 e
接近LRU,优先换出只读的页,使得读写硬盘的次数减少
5.7 最不常用法(Least Frequently Used,LFU)
访问次数最少,则换出
简单的使用计数器的弊端:
- 如果把计数器用一个寄存器来存,则硬件开销很大
- 如果对计数器进行查找,要查找链表,访问也很大,链表很长,就会早场访问时间过长
LRU和LFU的区别:
- LRU考察的是
多久未访问
,时间越短越好 - LFU考察的是
访问的次数或频度
,访问次数越多越好(问题:一个页面在进程开始时使用得较多,但以后就不使用了,实现也很费力,解决方法:没有考虑时间,如可以 定期将次数寄存器右移一位,即次数除以2)
问题:
(这个???)
time | Requests | ||||
0 | a->8 | b->5 | c->6 | d->2 | |
1 | c | a->8 | b->5 | c->7 | d->2 |
2 | a | a->9 | b->5 | c->7 | d->2 |
3 | d | a->9 | b->5 | c->7 | d->3 |
4 | b | a->9 | b->6 | c->7 | d->3 |
5 | e | a->9 | b->6 | c->7 | e->1 |
为什么不是替换d?定期除2?
5.8 Belady现象、LRU、FIFO、Clock的比较
Belady现象
Belady现象:在采用FIFO算法时,有时会出现分配的物理页面数增加,缺页率反而提高的异常现象
原因:并没有考虑到程序运行的动态性
- 分配物理页数为3
只有3次访问命中 - 分配物理页数为4
只有2次命中 - LRU不会产生这个问题(符合栈算法,未展开讲)
LRU、FIFO、Clock的比较
如果程序本身没有局部性特征,则LRU可能会退化为FIFO(LRU和FIFO和Clock会变得差不多)
Clock算法使用一个访问位
6 全局页面置换算法
6.1 局部页替换算法的问题、工作集模型
由上图可知,当分配3个物理页帧时,9个缺页,当分配4个物理页帧时,1个缺页数,物理页帧数目会对缺页数有影响
当对一个程序分配的物理页帧数目相同时,限制了程序产生缺页的特点,因为程序在运行过程中有阶段性,可能一开始需要很多内存,中间很少,最后又很多,对于物理页帧的需求是动态变化的。
之前都是假设分配的物理页帧的个数是固定的,假定os只能跑一个程序,那么就可以分配给它所有的物理页帧。但是如果不是只跑1个程序,如果给每个程序分配的物理页帧数固定,则这样是不灵活的,能不能根据程序运行的阶段动态分配物理页帧,是全局页面置换算法要考虑的问题
工作集模型
1 工作集(working-set)
工作集:一个进程当前正在使用
的逻辑页面集合
当前正在使用:是一个时间段,起始时间和它的一个持续长度,代表当前
逻辑页面集合:存了该程序这段时间内访问的页面
t:当前执行时刻
Δ:一个长度,一个定长的页面访问的时间窗口
t+Δ=1个时间段
t会变,而Δ不会变,w会变
t2那个局部性较好
t1那个有的部分局部性较好,有一定的局部性,如访问了4次7,而整体局部性不如t2
2 常驻集
常驻集:指当前时刻,进程实际驻留在内存当中的页面集合
常驻集 VS 工作集:
- 常驻集:当前运行的程序需要访问的页哪些在内存中
- 工作集:当前运行的程序需要访问的页是哪些,这些页可能在内存中,有可能不在,我们希望所访问的页都在内存中,一旦不在内存中就会产生缺页中断
不是分配给程序越多的物理页帧越好,当此程序运行时,有多余的物理页帧可以动态分配给其他程序,会降低缺页率。
给不同的程序分配不同的常驻集,缺页次数变少,性能提升,这样操作,局部页面算法是无法解决的
6.2 两个全局置换算法
6.2.1 工作集页置换算法
替换不在工作集里的页
随时间推移,不属于工作集的也会被扔掉
随着程序的执行,只要这个页不属于工作集窗口了,就可以把这个页丢掉
假如,上图的窗口大小为4
时刻 | 工作集 | |
0 | {a,d,e} | |
1 | {a,c,d,e} | 中断 |
2 | {a,c,d} | t=-2:e,t=-1:d,t=0:a,t=1:c,t=2:c而窗口大小为4,所以e就不在工作集里了 |
3 | {a,c,d} | |
4 | {b,c,d} | t:0:超出工作集窗口 |
{} |
与局部替换算法的区别:
当前在物理内存中放哪些页,取决于它是否在工作集窗口之内,超出工作集窗口的页面都会被换出去,所以可以确保物理内存中始终有足够的页存在,可以给其他程序提供足够的内存,从而减少页面置换的次数,使整个系统多个程序缺页次数降低
6.2.2 缺页率页面置换算法
窗口大小也可以变
使常驻集大小可变,一种更灵活的全局算法
依据是缺页的频度,缺页次数多,则分配更多的物理页;缺页次数少,则分配的物理页够多,则可以压缩常驻集
(当前产生中断的时刻-上一次产生中断的时刻)与 T的大小
tcurrent-tlast=这次产生缺页中断的时间t-上一次产生缺页中断的时间t
如t=4时,产生缺页中断,而上一次产生缺页的t=1,所以tcurrent-tlast=4-1=3
工作集的是得每一次都要判断是否超出工作集
而缺页的是只有在中断时才考虑
这2个都是动态调整,而局部的是只有分配的物理页帧满了的时候才调整
6.2.3 抖动问题
工作集代表着本身对内存访问的固有属性
常驻集是os当前把程序运行需要的页面放到内存
涉及到2个参数:
- 平均缺页时间(MTBF)
- 内存大小
红线:随着程序运行的越来越多,内存很快就用完了,就会产生很多缺页,也就需要大量的换入换出,使得操作系统把大量的时间都用在了换入换出上,所有的CPU时间都被操作系统用来换入换出了,从而导致整个系统的效率降低
需要找到最优点,使得跑的程序尽可能的多,同时系统利用率很高
同时还需要平均缺页时间(MTBF)与页缺失服务时间(PFST)尽量相等,比值越大说明缺页频度越低。
随着跑的程序越来越多,缺页中断发生的越来越频繁,即MTBF越来越小,而PFST不变,所以MTBF/PFST会越来越小
找到NI/O-balance 跑的程序很多,缺页数较少,系统利用率也可以保持较高的利用率
希望通过os的管理,使得系统利用率很高,系统中跑的程序也比较多,达到系统资源的充分利用
总结:
局部算法和全局算法与程序访问内存的访问序列有联系,和os分配给这个程序的页帧大小也有关。虚存需要这些算法来实现有效的缓解内存不够的现象,同时还要保证系统高效的运行