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