《操作系统导论》 内存虚拟化

内存虚拟化

一、地址空间

1.定义

运行的程序看到的系统内存,包含进程的所有内存状态

2.结构

程序代码

堆:动态分配,用户管理内存(比如new),长期需求,由程序员显式申请和释放

未分配

栈:函数电泳信息,局部变量,传递参数,函数返回值,短期需求,编译器隐式管理,反向增长,从函数退出时,编译器释放内存。

3.透明(内存虚拟化核心)

操作系统提供的抽象化假象不应该被应用程序看破

操作系统为每一个程序提供的虚拟内存都是从0k开始,但是物理位置却并不是这样,如何高效地为不同的地址空间分配真正的物理地址

二、地址转换

1.定义

硬件每次对内存访问进行处理时,都会从虚拟内存转换为真实的物理地址

内存管理单元(MMU):CPU中负责地址转换的部分

2.重定向

动态重定向(硬件):通过基址寄存器来找到对应的物理内存开始处,利用界限寄存器来限制进程能够访问的内存大小,每个CPU的内存管理单元都需要这两个寄存器

静态重定向(软件):利用加载程序的软件来接受将要运行的可执行程序,但是没办法提供访问保护(可能越到别人的内存)

3.硬件支持

  1. 提供特权模式:进而才能访问整个计算机资源
  2. 提供基址/界限寄存器:实现重定向的硬件
  3. 提供转换电路:实现地址转换及检查的电路
  4. 提供修改基址/界限寄存器的特权指令:在开始运行前,能够去设置
  5. 注册异常处理程序的特权指令:操作系统要告知硬件,如果发生异常,需要去执行哪些代码
  6. 能够触发异常:发现进程使用特权指令或者越界内存

3.操作系统工作

  1. 内存管理:创建分配内存,终止回收进程,利用空闲列表管理内存
  2. 终止进程:回收内存给其他进程和操作系统使用
  3. 基址/界限寄存器:由于一个CPU只有一个基址寄存器和界限寄存器,因此必须切换上下文保存和恢复寄存器
  4. 异常处理:操作系统启动时加载该类程序

4.受限直接执行过程

1.初始化(为中断、异常做准备,为内存初始化)

  1. 操作系统初始化陷阱表
  2. 硬件获得系统调用处理程序、时钟处理程序、非法内存处理程序、非常指令处理程序等地址
  3. 操作系统开始中断时钟
  4. 硬件上时钟中断
  5. 操作系统初始化进程表,空闲列表
2.正常运行(进程A的一生)
  1. 操作系统为进程A分配内存:进程表中分配,分配内存,设置基址/界限寄存器,从陷阱返回
  2. 硬件上恢复A的寄存器,跳转用户模式,设置PC
  3. 程序开始执行任务,代码获得指令
  4. 硬件转换虚拟地址获得指令
  5. 程序执行指令
  6. 硬件确定地址没越界,转换虚拟地址执行指令,加载保存
  7. 程序要进行系统调用
  8. 硬件发生时钟中断,转入内核模式,跳到中断处理程序
  9. 操作系统处理陷阱,将寄存器保存到进程结构,将新的进程从进程结构中恢复到寄存器,陷阱返回
  10. 硬件恢复B的寄存器,转向用户模式,跳到PC
  11. 如果程序运行过程发生错误
  12. 硬件加载越界,跳转到内核模式,跳到陷阱处理程序
  13. 操作系统处理本期报告,终止进程,回收内存,移除进程在内存中的地址

5.分析

优点:利用硬件支持,能够快速地将所有内存访问操作中的虚拟地址转换为物理地址

缺点:容易形成内部碎片(固定长度形成的间隙)

6.操作系统提供的内存解决方案

  • 分割成不同长度的段
  • 分割成长度相同的页

三、分段

1.问题

地址空间存在空余;地址空间大小确定可能会导致不够用

内部碎片太多了,其中没有被使用的第三段地址空间会形成大量的浪费

2.定义

定义将典型的地址空间分成三段:代码,堆,栈

每一段都有自己的基址和界限寄存器,则可以将不同段分散到不同的物理地址

3.表示方式

前两位:表示段的意义,代码,栈,堆(在一些系统中会将堆栈看成一端,进而只需要一位)

后几位:表示在该段中的位置,偏移量

4.保护位

为每个段增加几个位,标识程序能否读写该段或者执行其中代码,可以实现共享内存地址,允许某些段被共享

5.外部碎片

不同段之间不可避免地会存在一些空闲的部分

紧凑:终止运行的进程,将数据复制到连续的内存区域中

空闲列表管理法:根据相关的匹配算法,试图保留大的内存块用于分配

6.粒度

粗粒度:分为少量的大段

细粒度:分为大量的小段

7.缺点

外部碎片众多、段中稀疏的数据仍然会造成内部碎片(更多是指堆,栈的数据为自动分配)

四、空闲空间管理

1.目标

利用多种机制和策略尽可能让段的位置分配更加合理,进而解决分段后外部碎片众多的问题

2.机制

  • 分割:将大片的空闲区域分割成小区域,第一块给用户,第二块留在列表中

  • 合并:被分配给程序的内存在free之后,释放的内存会检查周围是否有相邻的内存块,如果有则会被合并成可用的空间块

3.实际分配内存结构

  • malloc库使用的头部:htpr指向,包含实际分配大小、幻数(正常性检验),即包含分配内存空间的相关信息
  • 返回给调用者的实际空间:ptr指向,为调用者实际获得空间
  • head指针:指向空闲列表的最新头部
  • 空闲列表头部:包含size(剩余空闲块的大小),next:下一个空闲块的位置

4.策略

  • 如何找到空闲列表中的分配空间起始位置,保证快速和碎片最小化
  • 最优匹配:找到所有空闲块中最小的能满足请求的块
  • 最差匹配:找到最大的空闲块,分割成用户请求的大小
  • 首次匹配:从头开始,找到第一个能够满足的块
  • 下次匹配:从上一次查找结束的位置开始,找到第一个能够满足的块

5.其他技术

  • 分离空闲列表:为具有特殊且频繁需求的应用单独开设一个列表—厚块分配程序:每次内核启动时,都分配一定的对象缓存(分离了特定大小的空闲列表),如果不够,则向通用内存申请一些内存厚块,如果对象缓存数量变为0,则回收部分内存到通用内存
  • 伙伴系统:(以二分伙伴分配程序为例)当有一个内存分配请求时,空闲空间被递归地一分为二,直到刚好可以满足请求,如果该块被释放,也会逆序完成合并

五、分页

1.定义

  • 将地址空间分成固定大小的页
  • 页帧:物理内存上划分的页,FP
  • 页:地址空间中固定长度的页,VP
  • 页表:操作系统为每一个进程保存的一个可能很大的数据结构,包含页表格条目(PTE):页,页帧;能够根据虚拟地址找到真实的物理地址,存储于内存中,但是也可以通过虚拟化内存,存储到磁盘上

2.虚拟地址

  • 经过计算公式的转换可以得到以下
  • 虚拟页面号(VPN):找到地址空间中的哪个页面,通过算法转换成物理地址VPN
  • 页内偏移量(offset):找到该页面中哪个字节

3.页表

  • PTE:记录VPN->FPN

  • 有效位:表明该位置指示的地址转换是否有效(堆栈之外的区域、尚未被使用的中间空间属于无效区域)

  • 保护位:该页能否被读取,写入或者执行

  • 存在位:该页在物理存储器上还是磁盘上,“交换”允许将少部分页面移到磁盘

  • 脏位:页面被代入内存后是否被修改过

  • 参考位:哪些页面被访问的很频繁

4.操作过程

  1. 根据虚拟地址得到VPN和offset
  2. 找到正在运行进程的页表所在的内存地址,包含基址寄存器
  3. 找到VPN对应的FPN,进而确定对应的页帧以及偏移位置

5.缺点

速度慢:由于需要进行一次VPN和offset的计算,同时还要到内存中进行一个页表的查询

浪费大:页表可能会造成很大的内存损失

六、快速地址转换(TLB)

1.目标

为了每次指令获取、显式加载存储或保存额外读取时造成的分页读取缓慢问题

引入地址转换旁路缓冲存储器(TLB),虚拟地址到物理地址的硬件缓存

2.操作

  • TLB:VPN|PFN|其他位,用户地址占地址空间的一半,剩余留给内核

  • 命中:那没事了

  • 未命中:硬件抛出异常,暂停当前指令,转换为内核态,跳转到操作系统控制的陷阱处理程序,遍历所有页表,将对应的项放入,然后再次执行查找指令(相较于以前上下文切换运行吓一跳指令),然后再引发一次查找,则会命中;放入项时还会考虑时间和空间局部性。

    ​ 需要注意的是:未命中,那么就要查找页表,但是运行这一系列操作的时候可能还是要找到对应地址,如果这些地址还是不在TLB中,那么就会出现TLB未命中的无限递归

  • 上下文切换:

    1. 在页表基址寄存器变化时,清空TLB(将全部有效位置0)
    2. TLB中加入进程标识符

3.替换策略

最近最少替换(LRU):字面意思

七、小表分页

1.目标

解决页表太大占据内存的问题

页表大小=log2(地址空间/页的大小)*页表项大小

2.更大的页

更大的页表当然可以缩小页表的大小,但是也会造成每一页内部碎片的浪费

3.分页和分段的结合

为堆、栈、代码分别提供一个页表,每个页表有一个基址寄存器和界限寄存器

能够省去为没有使用页的内存分配

4.多级页表

  • 将页表分成页大小的单元
  • 如果整页的页表项都无效,那么就不分配页表来表示
  • 页目录:告知页表的页在哪里,页表的整个页是否包含有效页
  • 二级页表:起始指针指向一个页目录,页目录中存储着PFN,其对应着不同页表的位置,再去对应页表中查出页的物理位置
  • 多级页表:由于页表是页的大小,如果地址空间过于大,页很小,那么二级页表可能就不够了,页目录索引1,2
  • 缺点:
    • 如果TLB未命中,那么需要更多次的查找

5.反向页表

保留一个页表,其中的项代表每个物理页,告知我们哪个进程在使用此页,该进程的哪个虚拟页映射到此页

八、超越物理内存

1.目标

利用大而慢的磁盘提供巨大虚拟地址空间的假象

2.交换空间

硬盘上用于物理页移入和移出内存的一部分空间

3.存在位

  • 页表项中的存在位标志位1表示该页位于物理内存中,如果标为0,那么表示在磁盘中,称为页错误
  • 页错误:TLB未命中,去页表中查询发现存在位为0,操作系统会接管该错误,进行页错误处理程序,将该页交换到内存中,然后发起查询,发现TLB中仍然没有,则进入内存查找,加载到TLB,最后发起一次查询,找到TLB对应的数据。
  • 先读入一个物理帧,然后从交换空间读取一个页,最后更新

4.TLB未命中

  1. 该页有效且存在,有效则说明地址转换合理,存在表示可以在内存中读取,一次查找
  2. 有效但不存在,两次查找
  3. 无效,报错,OS可能会杀死非法进程

5.交换守护进程

通过设置高水平线和低水平线来检测内存页中应该释放还是添加页

6.页替换策略

  • 最优替换策略:踢出最远将来才会访问的页,作为理想策略来评估其他策略
  • FIFO先进先出:字面意思
  • 随机
  • LRU:将最不经常使用的页踢出,需要进行很多的维护和操作
  • 近似LRU:增加一个使用位,进行页替换,如果某个页的使用位为1,表示该页最近被使用,则不替换,并将其使用位设置为0

7.其他技术和方法

  • 优先踢出干净页,而不是脏页
  • 预取:操作系统猜测某个页面即将被使用
  • 页选择:操作系统决定何时加载页
  • 聚集:合在一起写入,减少写入次数
  • 抖动:当进程的内存请求超额,OS需要进行的操作;比如准入控制:不运行部分进程

九、VAX/VMS虚拟内存系统

1.目标

确保页表不会占满内存

2.地址空间

用户空间P:P0用户程序和堆 P1栈,为P0,P1分别提供了一个页表,将该页表分配到内核虚拟内存中S

系统空间S:陷阱表,内核数据,内核代码,内核堆

上下文切换时,P0,P1的基址和变址寄存器发生改变,但是S不会改变,因此将“相同的”内核映射到每个用户的地址空间。两者的共存能够加快访问的速度,不需要反复的地址转换

3.页替换策略

  • 分段的FIFO:每个进程都有一个可以保存在内存中的最大页数,称为驻留集大小(RSS),当进程P超过RSS,那么会从其地址空间中FIFO地移除一个页,如果干净则放在干净页列表末尾,如果脏放在脏列表末尾;如果另一个进程需要空闲也,会先从干净页取出,如果TLB查找出现页错误,那么就会从脏列表查找
  • 页聚集:将大量的脏页和干净页存在列表中,同一写入

4.其他技巧方法

  • 按需置零:当进程请求页被添加到地址空间中,页中原有的数据并不会立刻清楚,而是会等待该页被访问时才会置零
  • 写时复制:如果操作系统要将一个页面复制到另一个地址空间,并不会直接复制,而是映射到目标空间,如果只是读,那么永远不会真的复制,直到写入事件的发生,页才会被真正的复制进去
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值