os-9.虚拟内存
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了os-9.虚拟内存相关的知识,希望对你有一定的参考价值。
9.虚拟内存
背景
虚拟内存——将用户逻辑内存与物理内存分开
- 只需要执行的部分程序调入内存
- 逻辑地址可以比物理地址空间大的多
- 允许多个进程之间共享地址空间
- 允许更有效的进程创建
按需调页
demand paging,在需要时才调入相应的页(即lazy swapper懒惰交换)
按需调页=分页+缺页+页置换
优点:更少IO,更少内存,更快反应,更多用户
基本概念
有效-无效位 valid-invalid bit,页表存储哪些页在内存中
对标记为i的页访问会产生缺页错误 page fault
处理缺页错误的程序如下:
- 检查页表确认该引用是否合法
- 引用非法则终止进程,引用有效则调入
- 找到一个空闲帧
- 交换页
- 重置页表
- 重新开始因缺页错误而中断的指令
按需调页的性能
设p为缺页错误的概率,则有效访问时间(EAT)=(1-p)*ma+p*页错误时间
ma为内存访问时间一般为200ns
页错误时间包括:1处理页错误中断,2页换入换出,3重启进程,一般为8ms
若页错误为1/1000,则EAT=8.2us,慢了近40倍
实现按需调页,需要考虑两个问题:帧分配,页置换
写时复制
copy-on-write,COW允许父进程和子进程共享页,只有在修改某一页时才对该页进行拷贝(需要写保护)
空闲页通常通过空闲缓冲池由按需填零技术分配页,ZFOD(zero-fill-on-demand)
页面置换
page replacement
当内存过度分配使无空闲页时,操作系统有如下选择
- 终止用户进程
- 交换出一个进程
- 页置换
基本页置换
页错误处理程序:
查找所需页在磁盘的位置
查找一个空闲帧
a. 若有空闲帧,直接使用
b. 若无空闲帧,使用页置换算法找到一个牺牲帧 victim frame
c. 将牺牲帧写入磁盘,改变页表和帧表
将所需页读入到空闲帧,改变页表和帧表
重启用户进程
页置换过程如下:
可以通过修改位或脏位来降低开销(modify bit,dirty bit)。若修改位没有设置,当该页被选为替换页时,则无需写入磁盘,降低了一半的I/O
FIFO页置换
使用队列管理,队首的页被置换
Belady异常,当帧数增加时,页错误率不一定总是下降
最优置换
Optimal page-replacement,OPT错误率最低,没有Belady异常
从未来看,替换最长时间不会使用的页
缺点:难以实现
LRU页置换
least-recently-used algorithm,最近最少使用算法
从历史看,替换最长时间不会使用的页
实现
- 计数器:每一项关联时间项,每次内存引用,将计时器的值赋给新的页,选择最小的页置换
- 栈实现:每次替换栈底
注意上述两种方式对无页替换时仍需设置相应的值,如下面栈的模拟
近似LRU算法
通常没有足够的硬件支持LRU算法,更简单的情况,对每一页设置一个引用位 reference bit,来表示是否被使用
- 附加引用位算法
为每一页保留一个8位字节,在规定的时间段后,os把每个页的引用位转移到8位字节的最高位,右移其他位。则8位寄存器保存了过去8个周期内的使用情况,每次置换值最小的页(更大的值意味着更近被使用)
- 二次机会算法
若引用位为0,则替换
若引用位为1,则改为0,放进内存队列,选择下一页
- 增强型二次机会算法
将引用位和修改位共同考虑,按照(0,0)-(0,1)-(1,0)-(1,1)的顺序来置换
基于计数的页置换
- 最不经常使用页置换算法LFU:置换最小计数的页
- 最常使用页置换算法MFU:最小次数的页刚调进来且还没有使用
帧分配算法
给每个进程分配多少帧内存的问题
帧的最少数量
越少页错误越多,至少有足够的帧完成单个指令
最少数量由体系结构决定,最大由物理内存决定
分配算法
- 平均分配
- 比例分配 proportional allocation
根据进程的大小划分比例
根据优先级划分比例
全局分配与局部分配
全局置换:从所有帧集合中选择一个置换帧
局部置换:每个进程仅从自己的分配帧中进行选择
全局置换可能从低优先级的进程抢帧,增加自己的帧数量。不能控制页错误率。但通常系统吞吐量更好,更常用
系统颠簸
Thrashing(进程没有足够的帧,产生页错误,选择一个正在使用的页置换,但很快又再次需要这一页。这样反复产生页错误,置换页)频繁的页调度行为,在换页上花费的时间多于执行的时间
原因
操作系统发现cpu使用率低,引入新进程
新进程需要更多帧,出现页错误,从其他进程拿到帧,这些进程再从另外进程拿帧
就绪队列变空,进程等待调页设备
cpu使用率降低,引入更多的进程,更多的页错误,更长的队列等待调页设备
出现系统颠簸,吞吐量下降,页出错显著增加
解决:分配满足进程当前局部的帧
工作集合模型
设\(\Delta\)为工作集合窗口,检查在过去\(\Delta\)个页的引用的集合定义为工作集合,如图\(\Delta\)为10时
每个进程的工作集合大小为\(WSS_i\),则总的帧需求量D
\[
D=\sum WSS_i
\]
当D大于可用帧的数量则可能出现颠簸,即系统控制总帧数少于可用帧的总数即可
系统追踪每个进程的工作集合,并为其分配大于工作集合的帧数,若还有空闲帧则启动另一个进程。若所有工作集合之和的增加超过了可用帧的总数,则选择暂停一个进程,该进程的页被写出,且其帧分配给其他进程。挂起的进程可稍后重启
跟踪工作集合:
若\(\Delta\)取10000,
- 5000个引用产生定时中断
- 每一页设置两位
- 中断时复制所有引用,清除所有引用位
- 若有一位为1,则认为处于工作集合中
页错误频率
page-fault frequency,PFF,解决颠簸的另一种方法
若实际错误率超过上限,则分配更多的帧;若低于下限,则从该进程中移走帧(利用率不足)
内存映射文件
memory mapping,运用虚拟内存技术,将文件I/O作为内存访问
基本机制
通过内存操作文件而不是系统调用read()和write(),简化了文件访问和使用
多个进程间文件的共享
windows的实现方式
内核内存的分配
一般通过空闲内存池分配
- 内核有些数据结构不需要一页内存
- 有的需要连续内存
Buddy系统
按照2的幂来分配
优点:可通过合并快速形成更大的段
缺点:容易产生碎片(33KB只能用64KB来满足)
slab分配
slab由一个或多个物理上连续的页组成,cache含有一个或多个slab,每个内核数据结构有一个cache
优点:无碎片(slab与对象大小相等),内存请求可快速满足(slab预创建)
其他考虑
预调页
prepaging,问题在于采用预调页的成本是否小于处理相应页错误的成本
页大小
- 碎片——更小页
- 页表大小——更大页
- I/O花费——更大页
- 局部性——更小页
- 页错误——更大页
总的来说趋向更大页
TLB范围
TLB条数与页大小之积
通常,工作集合位于TLB中
一味增加页大小会造成碎片化,可提供多种页大小
程序结构
Program 1
for (j = 0; j <128; j++)
for (i = 0; i < 128; i++)
data[i,j] = 0;
128 x 128 = 16,384 page faults
Program 2
for (i = 0; i < 128; i++)
for (j = 0; j < 128; j++)
data[i,j] = 0;
128 page faults
I/O互锁
用于I/O的页必须不被替换算法替换,即允许页锁在内存中——lock bit
操作系统实例
Windows XP
通过簇来实现虚拟内存 clustering
- 进程创建时设置工作集合最小值和最大值
- 若进程的页数低于工作集合最小值且发生页错误,则分给更多帧;若进程的页数达到工作集合最大值且发生页错误,则采取局部置换
- 当空闲内存的量低于阈值时,采用自动工作集合修整
若进程分配的帧数大于工作集合最小值,则删除帧知道进程等于最小值,一旦有足够的空闲内存,再分配给帧数为工作集合最小值的进程
Solaris
os需要维护足够多的空闲内存
- 空闲页数低于lostsfree(1/64),启动换页进程,每秒检查4次
双指针轮转算法:时钟的第一个指针扫描内存所有页,将其引用位置0,之后时钟的第二个指针将引用位仍为0的页加到空闲链表
- 空闲页低于desfree,每秒启动100次换页进程,30秒内不能增加超过desfree,则开始交换进程(失去该进程所有页)
- 空闲页低于minfree,每次请求新页时换页
以上是关于os-9.虚拟内存的主要内容,如果未能解决你的问题,请参考以下文章
如何使用模块化代码片段中的LeakCanary检测内存泄漏?
java内存流:java.io.ByteArrayInputStreamjava.io.ByteArrayOutputStreamjava.io.CharArrayReaderjava.io(代码片段