操作系统学习笔记之虚拟内存分页相关

450 阅读7分钟

最近看了《现代操作系统》、《深入理解计算机系统》和《操作系统导论》,因为书都很厚,重点先学习了虚拟内存、分页机制相关知识,特写此博客作为学习笔记方便日后及他人复习


虚拟内存

一些知识点

虚拟内存的三个重要能力

  • 将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据。通过这种方式,它高效地使用了主存
  • 它为每个进程提供了一致的地址空间,从而简化了内存管理
  • 它保护了每个进程的地址空间不被其他进程破坏

虚拟寻址

虚拟寻址:CPU通过生成一个虚拟地址(Virtual Address,VA)来访问主存,这个虚拟地址会被MMU(Memory Manage Unit 内存管理单元)通过放在主存中的查询表来翻译成物理地址

在主存中的每一个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址

虚拟内存地址空间按照固定大小划分成若干单元,被称为page ;物理地址空间被划分称为若干单元,被称为page frame;两者大小通常是一样的

页表

页表将虚拟页映射到物理页。存在于物理内存中

页表就是一个页表条目(Page Table Entry,PTE)的数组,相当于页表中的每个格子就是一个PTE

MMU中16位的虚拟地址 = 4位页号 + 12位偏移量

PTE中包括:page frame号,高速缓存禁止位,访问位,修改位,保护位,present/absent位

缺页中断

就是MMU发现该页面没有被映射。那么操作系统需要找到一个很少使用的页框且把他的内容写入磁盘(如果他不在磁盘中),随后把需要访问的页面读到刚才回收的页框中,修改映射关系,重新执行引发缺页中断的指令

局部性原则

局部性原则保证了在任意时刻,程序将趋向于在一个较小的活动页面集合上工作,这个集合叫做工作集,或者常驻集合。在初始开销,也就是将工作集页面调度到内存中之后,接下来对这个工作集的饮用将导致命中,而不会产生额外的磁盘流量。

抖动

如果工作集的大小超出了物理内存的大小,那么程序将发生抖动,页面会不断地换进换出。

TLB

如果每次都需要通过访问页表才能访问到目标内存,那么相当于两次访问内存才能实现依次内存访问,这就会使性能下降一半

经过观察发现,大多数程序总是对少量的页面进行多次的访问,因此解决方案是设置一个小型硬件设备将虚拟地址直接映射到物理地址而不需要通过页表(相当于再一层缓存),这种设备叫转换检测缓冲区(Translation Lookaside Buffer,TLB),表项中也包含修改位、保护位、虚拟页号

TLB软失效

当一个页面访问在内存中而不在TLB中,将产生软失效。此时只需要更新一下TLB不会产生磁盘I/O

TLB硬失效

当一个页面不存在内存中也不存在TLB中,产生硬失效。此时需要依次磁盘存取以装入该页面

多级页表和倒排页表

如果内存需要特别大,那么页表也会变得很大,可以采用多级页表的方式。 倒排页表页可以节省大量的空间,但从虚拟地址到物理地址的转换将变得很困难,必须要搜索整个倒排页表。

可以用TLB加速来解决倒排页表的缺陷,当TLB失效的时候,建立一张散列表,用虚拟地址来散列,加速新的地址存入TLB的过程

多级页表倒排页表这里还没有完全理解,学懂了我再来详细写

页面置换算法

发生缺页中断时候,如何确定淘汰哪个页面?

最优页面置换算法

是一种理想的,不可能被实现的算法

思想是:我们可以知道每个页面还需要经过多少指令以后才会被访问到(事实上是做不到的),就是直到页面被访问的先后顺序,然后把需要等最久才被访问的页面置换出去

最近未使用页面置换算法

通过给页添加标志位,分为四类页面 0. 没有被访问,没有被修改

  1. 没有被访问,已经被修改
  2. 已经被访问,没有被修改
  3. 已经被访问,已经被修改

从类号小的里面随机挑一个置换,中心思想是“尽量删除没被访问的,已经被访问但没被修改也说明可能经常用(局部性原则),所以要留着”

先进先出页面置换算法

FIFO first in first out 很好理解,但因为可能会把经常用但页面换出去,所以不大好

第二次机会页面置换算法

因为FIFO可能把常用的换出去,那就在换出去之前检查一下R位置,如果是1表示被使用过,那就放到末尾再去排队再给他一次机会,如果是0从来没用过那就直接换出去

时钟页面置换算法

上面但第二次机会算法虽然比较合理,但因为维护的是链表,经常在链表中移动页面效率很低,所以可以改进一下,把链表改为环形链表,用指针来指向最古老的页面,然后再看R位置。

其实除了链表成环没什么变化,动的是指针不是页面,效率高点

最近最少使用页面置换算法LRU

LRU虽然理论上可以实现但实际代价很高,毕竟每次访问内存,必须要更新整个链表。找到一个页面然后移动到表头也很费时间

可以在硬件上实现,用一个计数器C,每次执行完一条指令,自动加1,然后把这个值存在对应页面上,缺页中断时候找到值最小但哪个页面,这个就是最近最少使用的页面

最不经常使用页面置换算法NFU/老化算法

这个没仔细看懂,以后补,大概是对LRU的粗略实现,LRU实际上是很难实现的

工作集页面置换算法

跟刚才说的局部性原则有关,基本思路“找出一个不在工作集中的页面换出去”

建议看《现代操作系统》P121-122

工作集时钟页面置换算法

对上一个的改进,把数据结构改成环形链表

《现代操作系统》P123

页面置换算法对比总结

《现代操作系统》P124写得很详细

缺页中断处理

当缺页中断发生时,系统做了什么

《现代操作系统》 P131 3.6.2小节


结束语

这一篇书中讲的很详细,有些部分书里写的浅显易懂不需要我再重复总结就没有抄书。 下一周希望可以学习总结一下进程通信,线程,死锁,分段机制,文件系统I/O

加油,不要去追马,用追马的时间种草。