读《计算机的心智》,另一个角度看操作系统

基本内存管理

内存架构:缓存——主存——磁盘

image

内存管理机制负责对内存架构进行管理抽象,使程序在内存架构的任何一个层次上存放对于用户都是无感知的。

内存管理目标

地址保护:一个程序不能访问另一个程序的地址空间 地址独立:程序发出的地址与物理主存地址无关

虚拟内存

将物理主存扩大到大容量的磁盘上(将磁盘空间看作主存空间的一部分)

多道编程的内存管理

固定分区策略

思想:将内存固定分为几个区域,每个区域大小固定。每个分区对应一个队列,程序按照大小排在相应的队列里进行加载。

image

非固定分区策略

思想:除了划分给操作系统的空间外,其余的空间作为一个整体存在,一个程序加载划分出一块内存。操作系统使用基址和极限管理。

image

交换(swap)[解决程序加载到内存后继续扩大,超过预留空间]:

交换就是将一个进程从内存倒到磁盘,等待再将其从磁盘加载到内存的过程。目的是为程序找到一片更大的空间,防止程序因为空间不够而崩溃。

重叠(overlay)[解决程序占用空间超过物理内存]:

重叠就是将程序按照功能分成一段一段完整的单元,一个单元执行完再执行下一个单元(后续单元可以覆盖之前的单元)

闲置空间管理

  1. 给每个分配的单元赋予一个字位用于记录是否空闲
  2. 链表表示法

离散分配内存管理

基本内存管理为进程分配的空间是连续的,使用的地址都是物理地址。如果允许将一个进程分散到许多不连续的空间,就可以避免内存紧缩,减少碎片。基于这一思想,通过引入进程的逻辑地址,把进程地址空间与实际存储空间分离,增加存储管理的灵活性。

地址空间:将源程序经过编译后得到的目标程序,存在于它所限定的地址范围内,这个范围称为地址空间。地址空间是逻辑地址的集合。

存储空间:指主存中一系列存储信息的物理单元的集合,这些单元的编号称为物理地址存储空间是物理地址的集合。

根据分配时所采用的基本单位不同,可将离散分配的管理方式分为三种: 页式存储管理、段式存储管理和段页式存储管理。其中段页式存储管理是前两种结合的产物。


页式内存管理

核心

页式内存管理核心:将虚拟内存空间和物理内存空间皆划分为大小相同的页面(4k,8k…),以页面作为内存空间最小分配单位。

分页系统下,程序发出的虚拟内存由页面号和页面偏移值组成(32位寻址系统,页面大小4k,页面号20位,偏移值12位)

image

页表

在页式系统中进程建立时,操作系统为进程中所有的页分配页框。当进程撤销时收回所有分配给它的页框。在程序的运行期间,如果允许进程动态地申请空间,操作系统还要为进程申请的空间分配物理页框。操作系统为了完成这些功能,必须记录系统内存中实际的页框使用情况。操作系统还要在进程切换时,正确地切换两个不同的进程地址空间到物理内存空间的映射。这就要求操作系统要记录每个进程页表的相关信息。

image

物理页面表:整个系统有一个物理页面表,描述物理内存空间的分配使用状况(通过比特位01表示是否空闲)

请求表:整个系统有一个请求表,描述系统内各个进程页表的位置和大小,用于地址转换也可以结合到各进程的PCB(进程控制块)里。

image
image

页表记录内容:

image

地址翻译(虚拟地址转换为物理地址)

通过产生系统中断(缺页中断),将虚页从磁盘上转移到内存,然后将分配给他的物理页面号返回。通过内存管理单元MMU完成(MMU接受CPU发出的虚拟地址,翻译为物理地址发送给内存,内存按照物理地址进行数据的读写)。

MMU翻译方式: 每个程序,内存管理单元都为其保存一个页表,通过查询进程表获得页表页表存放虚拟页面到物理页面的映射。通过虚拟地址的页号获取到页表的页号,页号映射的物理地址+页内偏移地址=物理地址

image

多级页表 顶级页表存放一级页表信息,一级页表存放二级…(顶级页表常驻内存,次级页表存放物理内存)Linux采用三级页表。

目的减少空间占用,大部分次级页表存放到磁盘。缺点是增加内存访问,磁盘访问。(缓存优化提高翻译速度)

页面更换算法

关于页面更换:如果发生缺页中断,就需要从磁盘上将需要的页面调入内存。如果内存没有多余的空间,就需要从页面中选择一个页面进行替换。

页面更换算法目标:如果更换的页面很快又被访问,系统会很快再次产生缺页中断(缺页中断代价很大)。页面更换算法的目标是降低随后发生缺页中断的次数。

公平算法👇

随机更换算法

略…

FIFO算法 更换最早进入内存的页面。使用链表将所在内存的页面按照进入时间的早晚链接起来,每次置换链表头的页面即可。

image

缺陷:如果最先加载进来的页面经常被访问,会降低效率。

第二次机会算法 改进FIFO算法,在更换链表头的页面时,查看是否在最近被访问过,没访问过则替换,访问过则挂载到链表尾。

缺陷:移至链表尾部消耗时间,时间分辨粒度低,影响页面替换效果。

时钟算法(第二次机会算法改进) 把页面排成一个时钟的形状。该时钟有一个针臂。每次需要更换页面时,我们从针臂所指的页面开始检查。如果当前页面的访问位为 0 , 即从上次检查到这次,该页面没有被访问过,将该页面替换。如果当前页面被访问过,那就将其访问位清零,并顺时针移动指针到下一个页面。我们重复这些步辍直到找到一个访问位为 0 的页面。

image

缺陷:时间分辨粒度低,影响页面替换效果,可能无限循环。

非公平算法👇

NRU算法(最近未使用算法) 选择最近一段时间没被访问过的页面进行替换,利用页面的访问和修改位。 当对页面进行读写操作时,访问位设置为1,进程对页面进行写操作时,修改位设置为1。

LRU算法(最近最少使用算法) 对NRU进行改进,不仅考虑是否使用过,还需要考虑频率。

使用位移寄存器实现:…

缺陷:保留了每个页面每次的访问记录,导致空间成本高。

工作集算法 维持少量信息选出最近最少使用。为页表的每个记录增加一项信息用来记录该页面最后一次被访问的时间。

  1. 如果一个页面的访问位是1,则将该页面的最后一次访问时间设为当前时间,并将访问位清零。
  2. 如果页面的访问位为0,则查看其访问时间是否在当前时间减去 T 之前。是则替换

image

段式内存管理

页式内存管理的缺陷是共享困难,一个进程只能占有一个虚拟地址空间。(一个程序的大小至多只能和虚拟空间一样大),段式内存管理就是为了解决这些问题。

关于分段管理 在段式存储管理中,将程序的地址空间划分为若干个段(segment),这样每个进程有一个二维的地址空间。在前面所介绍的动态分区分配方式中,系统为整个进程分配一个连续的内存空间。而在段式存储管理系统中,则为每个段分配一个连续的分区,而进程中的各个段可以不连续地存放在内存的不同分区中。程序加载时,操作系统为所有段分配其所需内存,这些段不必连续,物理内存的管理采用动态分区的管理方法。(相比基本内存管理,基本内存管理一个程序只有一个段,分段管理有多个段)

程序通过分段划分为多个模块,如代码段、数据段、共享段:

–可以分别编写和编译 –可以针对不同类型的段采取不同的保护 –可以按段为单位来进行共享,包括通过动态链接进行代码共享

这样做的优点是:可以分别编写和编译源程序的一个文件,并且可以针对不同类型的段采取不同的保护,也可以按段为单位来进行共享。

段式内存管理下,程序发出的虚拟内存由段号和段内偏差组成

image

分段实现方式

使用一组基址与极限对,每个基址与极限用于其中一段的管理。逻辑分段,每个虚拟地址只需要对其对应的基址寄存器与极限寄存器值进行调整,就可以加载到物理内存的任何空间。

image

image

地址翻译(虚拟地址转换为物理地址) 段表存放虚拟段号到该段的所在的内存基址的映射。物理地址=内存基址+段内偏差

image

缺陷 和基础内存管理一样,存在外部碎片和一个段必须全部加载到内存中,所以就有了段页式内存管理。

段页式内存管理

段页式管理就是将程序分为多个逻辑段,在每个段里面又进行分页,即将分段和分页组合起来使用。这样做的目的就是想同时获得分段和分页的好处,但又避免了单独分段或单独分页的缺陷。如果我们将每个段看做一个单独的程序,则逻辑分段就相当于同时加载多个程序。

实现

在段里面分页(次级页表),一个程序对应多个次级页表,在多个次级页表上增加一层段表(顶级页表)。由段表获取所应该使用的页表,通过页表查找物理地址。

image

成长过程

基本内存管理 缺陷:内存空间增长困难,外部碎片,程序不能超过物理内存容量,一个程序必须加载到内存才可以执行。

👇

页式内存管理 缺陷:一个程序只能在一个虚拟地址空间增长。

👇

段式内存管理 缺陷:解决了:一个程序只能在一个虚拟地址空间增长,但存在外部碎片和一个段必须全部加载到内存中(因为是基本内存管理的升级)

👇

段页式内存管理

image