虚拟内存笔记
虚拟内存
前言
基本假设:进程的代码和数据只有部分装入内存
虚拟内存技术允许将大逻辑地址空间映射到小的物理内存。虚拟内存允许极大的进程运行,且提高了多道程序的程度,增加了CPU利用率。它使得程序员不必考虑内存可用性。另外,虚拟内存允许进程共享系统库与内存。当父子进程共享内存页时,虚拟内存也允许写时复制来创建进程。
一、 背景
有些情况下不需要将整个程序放入内存中:
- 程序中有处理异常错误条件的代码
- 数组、链表和表通常分配了比实际所需要的更多的内存
- 程序某些选项可能很少使用
虚拟内存的好处:
- 使程序不再受现有的物理内存空间限制
- 提高了多道程序的程度,增加了CPU利用率
- 虚拟内存也允许文件和内存通过共享页而为两个或多个进程所共享,这带来了以下优点:
a) 通过将共享对象映射到虚拟地址空间,系统库可为多个进程所共享。
b) 虚拟内存允许进程共享内存
c) 虚拟内存可允许在系统调用fork()创建进程期间共享页,从而加快进程创建
二、 按需调页:在需要时才调入相应的页
- 交换程序对整个进程进行操作,而调页程序只是对进程的单个页进行操作(采用懒惰交换,只有在需要页的时候,才将其调入内存)
- 当换入进程时,调页程序推测在该进程再次换出之前会用到哪些页。这样就可以避免读入那些不适用的页,也减少了交换时间和所需的物理内存空间
- 有效-无效位:1表示页合法且在内存中,0表示页无效且在磁盘上。初始值为0,试图访问为0的页时会发生页错误陷阱(在内存管理中1表示有效,0表示无效)。当访问无效页面时,不会对MMU进行地址转换
- 对标记为无效的访问会产生页错误陷阱。分页硬件,在通过页表转换地址时,将发现已设置了无效位,会陷入操作系统。这种陷阱是由于操作系统未能将所需的页调入内存引起的。
- 是否合法需要在中断程序中判断
- 长期调度的时候会决定哪些页放入内存,哪些放在磁盘
-
页错误陷阱的处理:
a) 检查进程的内部页表(通常与PCB一起保存),以确定该引用是合法还是非法的地址访问。该判断在中断程序中进行。
b) 如果引用非法,那么终止进程。如果引用有效但是尚未调入页面,那么现在应调入。
c) 找到一个空闲帧(例如,从空闲帧列表中选取一个)。
d) 调度一个磁盘操作,以便将所需要的页调入刚分配的帧。是磁盘I/O采用DMA方式进行,会将进程放入等待队列
e) 当磁盘读操作完成后,修改进程的内部表和页表,以表示该页已在内存中。I/O完成
f) 重新开始因陷阱而中断的指令。进程现在能访问所需的页,就好像它似乎总在内存中。进程状态改变 - 通过第一次中断(缺页异常引发的中断)陷入操作系统,操作系统启动I/O操作,I/O操作完成引发第二次中断
- 有的指令(如MOV)可能会访问多个(三个)页的内存(一页指令,两页数据)
- 页表:该表能通过有效无效位将条目设置为有效/无效
- 次级存储器:存储不在内存的页,一般为快速磁盘,即交换空间
- 请求调页的关键是能够在页错误之后重新执行指令
- 性能:设P为页错误的概率(0≤P≤1),如果P等于0,则不存在页错误,如果P等于1,则每次访问都存在页错误。有效访问时间(EAT)=(1-P)内存访问时间+P页错误时间
-
交换空间的两种使用方法:
a) 进程在创建时将整个文件镜像复制到交换空间中,并从交换空间进行按页调度
b) 从文件系统中按需调页,页置换时将页写入交换空间 - 页错误会引起如下序列的动作产生:
a) 陷入到操作系统
b) 保存用户寄存器和进程状态
c) 确定中断是否为页错误
d) 检查页引用是否合法并确定页所在磁盘的位置
e) 从磁盘读入页到空闲帧中
i. 在该磁盘队列中等待,直到处理完读请求
ii. 等待磁盘的寻道和或延迟时间
iii. 开始将磁盘的页传到空闲帧
f) 在等待时,将CPU分配给其他用户(CPU调度,可选)
g) 从I/O子系统接收到中断(以示I/O完成)
h) 保存其他用户的寄存器和进程状态(如果执行了第6步)
i) 确定中断是否来自磁盘
j) 修正页表和其他表以表示所需页现已在内存中
k) 等待CPU再次分配给本进程
l) 恢复用户寄存器、进程状态和新页表,再重新执行中断的指令 - 以上不是所有的步骤都是必须的。
- 三个主要的页错误处理时间:
a) 处理页错误中断
b) 读入页
c) 重新启动进程
三、 写时复制
(1) 允许父子进程开始时共享同一页面,如果任一进程需要对该页进行写操作,则创建一个共享页的拷贝。所有非修改页会被父进程和子进程共享
(2) 写时复制需要的空闲页来自一个空闲缓冲池,该池中的页在分配前先填充0,以清除之前的内容(按需填零)
Vfork()(虚拟内存fork)是系统调用fork()的变种,操作不同于写时复制的fork(),vfork()会将父进程挂起,子进程使用父进程的地址空间。由于vfork()不使用写时复制,因此如果子进程修改父进程地址空间的任何页,那么这些修改过的页在父进程重启时是可见的。所以,vfork()必须小心使用,以确保子进程不修改父进程的地址空间。Vfork()主要用于在子进程被创建后立即调用exec()的情况
四、 页面置换
(1) 基本页面置换
- 查找所需页在磁盘上的位置
- 查找一个空闲帧:
如果有空闲帧,那么就使用它
如果没有空闲帧,那么就使用页置换算法以选择一个**“牺牲”帧**
将“牺牲”帧的内容写到磁盘上,改变页表和帧表 - 将所需页读入(新)空闲帧,改变页表和帧表
- 重启用户进程
通过修改位或脏位来降低开销,表示是否需要重写到磁盘上的副本
需要开发帧分配算法与页置换算法,分别决定一个进程分配多少帧以及如何选择要置换的页
通过运行一个特殊字符串(引用串)来检验算法并计算页错误率
(2)FIFO页置换
- 记录每个页进入内存的时间,并选择最老的页换出
- Belady异常:页错误率可能会随着分配帧的数目的增加而增加,而不是一般情况下的降低
(3)最优页置换,OPT/MIN
- 是所有算法中产生页错误率最低的,且不会有Belady异常的情况
- 置换最长时间不会被使用的页(向前看)
- 缺点:程序在执行时不知道未来要访问的页,所以难以实现
(4) LRU页置换:
即最近最少使用算法,没有Belady异常,和最优置换一样属于栈算法
- 置换最长时间内没有被使用的页,这种策略为向后看(而不是向前看)最优页置换算法
- 计数器:可以通过为每一个页表项关联一个时间域,并给CPU增加一个计数器,每次内存引用,为对应页表项的时间域进行更新,置换最小的页
- 栈:或者通过页码栈,每次将被引用的页从栈中删除并重新放回顶部,这样栈的顶不必定时最近使用的页,而底部为LRU页
(5) 近似LRU页置换
1、附加引用位算法:每页关联一个引用位,初始化为0,当页被引用的时候,改为被设置为1。引用位可以有多个,比方说有8位引用位,就表示着该页在最近8个时间周期内被使用的情况。而11000100比01110111的页更为最近使用。每次选择8位最小的一个页为LRU页,进行置换。
2、二次机会算法:基于FIFO算法。当选择时,检查引用位,如果为0则直接进行置换,否则会给第二次机会,并将引用位清零,到达时间设置为当前时间。
(6)基于计数的页置换
为每个页保留一个用于记录其引用次数的计数器,有两种算法:
① 最不经常使用页置换算法(LFU)
② 最常使用页置换算法(MFU)
五、 帧分配:
即每个进程正常执行都需要一个最小的帧数,性能和页置换
- 给每个进程分配最低数量的页,必须有足够的帧容纳所有单个指令所引用的页,而帧的最少数量是由计算机定义的。
- 指令是6字节,可能跨2页
- 要移动的字符的块和要移动到目的的区域也可能都要跨页。
- 有两种主要的分配方法:
①固定分配
②优先级分配
(一)、固定分配
- 有两种:
①平均分配
②按比例分配 - 每个进程所分配的数量会随着多道程序的级别而改变,多道程序的程度增加(内存中进程的数量增加),那么每个进程会失去一些帧来给新的进程
(二)、优先级分配
- 按优先级比例而不是进程大小来分配
- 如果一个进程产生了一个页错误,那么可以:
①从自身的帧中选择用于替换
②从比自身优先级低的进程中选取帧用于替换
三)、全局分配与局部分配
- 全局置换:允许一个进程从所有帧集合中选择一个置换帧,而不管该帧是否已分配给其他进程;一个进程可以从另一个进程中取帧。通常会有更好的系统吞吐量,且更为常用
- 局部置换:要求每个进程仅从其自己的分配帧中进行选择
六、 系统颠簸
- 如果一个进程没有足够的页,那么会一直忙于将页面换进换出,页错误率就会非常高。这会导致CPU使用率低,新的进程会加入到系统中来。
- OS发现CPU使用率低,会加大多道程序程度,使更多进程加入到内存,使页错误率更高,最终系统无法完成工作
- 局部置换可以限制颠簸在进程之间传递与扩散,而不能解决
- 工作集合策略研究一个进程实际正在使用多少帧,定义了进程执行的局部模型。
- 当进程执行时,它从一个局部移向另一个局部。局部是一个经常使用页的集合。一个程序通常由多个不同局部组成,它们可能重叠。
- 当一个子程序(函数)调用的时候,就定义了一个新局部
- 局部是由程序结构与数据结构来定义的,如果分配的帧少于局部的大小,进程会颠簸
(一)、工作集合模型:基于局部性假设
- 定义工作集合窗口,检查最近a个页面的引用,即工作集合。如果正在使用,则保留,否则,会在上次引用的a个时间单位后删除
- 工作集合模型是程序局部的近似
- 精确度与a的大小有关,如果a太小,则不能包含整个局部。若太大,可能包含多个局部
- WSSi (进程Pi的工作集) = 最近所引用的所有页面的总数(不同时刻值不同)
- D = ∑WSSi 表示总的帧需求量。如果 D 大于可用帧数量,那么有的进程就得不到足够帧,从而会出现颠簸
- 这时可以悬挂某些进程,以消除颠簸现象
- OS会跟踪每个进程的工作集合,并分配大于其工作集合的帧数。如果有空闲帧,则可以启动另一个进程
- 通过固定定时中断和引用位,能够近似模拟工作集合模型
(二)、页错误频率
- 可以灵活地控制颠簸
- 如果实际的页错误频率太高,则分配更多的帧,如果太低,就可以从进程中拿走帧
- 如果太高而又没有可用的空闲帧,那么就选择一个进程进行暂停,将其的帧进行释放,并分配给错误率高的进程
七、 其他考虑
- 页的大小如何选择:
①若考虑碎片,则需要小页
②若考虑页表大小,则需要大页
③若考虑I/O开销,则需要大页
④若考虑局部性,会有两个极端的矛盾。更小的页应用导致更少的I/O和更少的总的分配内存;而为了降低页错误数量,需要大页。 - TLB范围:通过TLB可访问的内存量
- 在理想的时候,一个进程所有的工作集合均在TLB中