第四章 存储器管理

写在前面: 这一章的内容主要是讲解存储的管理,其中包括以下几个方面

  • 程序的装入与链接
  • 连续分配方法
  • 离散分配方法
    • 分页式
    • 分段式
    • 段页式
  • 虚存技术
    • 分页实现
    • 分段实现
  • 页面置换算法
  • 共享与保护的实现

一、程序的装入与链接

​ 总所周知的,一个源代码如何生成一个可执行文件,需要经过如下步骤:

  • 编译,最后生成.obj文件
  • 链接,将目标文件与库函数链接在一起,形成一个装入模块
  • 装入,将装入模块放入内存当中执行

1.1 程序的装入

1.绝对装入方式:

​ 装入模块就用实际地址,装入预定的位置,但是可想而知,只能用于单道处理。

2.可重定位方式:

​ 装入模块为相对地址,由可重定位的程序在装入时再修改为绝对地址,适合于多道处理。而且这种方式在修改为绝对地址之后就不再发生变化,称为静态重定位。

3.运行时动态装入方式:

​ 先把程序装入内存中,等程序运行的适合,再进行地址的转化,这种需要特定的硬件来支持。

1.2 程序的链接

1.静态链接:

​ 直接把所有的模块装在一起,对相对地址进行修改,同时变换符号调用,对于相同的模块会重复链接。

  • 简单
  • 但是不易共享

2.装入时动态链接:

​ 总之就是用一个共享区。在windows中,根据输入库将DLL输出函数链接到应用中,可直接调用。个人理解就是在一个项目中,输入输出函数是只链接一次的。

  • 便于修改与更新
  • 便于对目标模块的共享

3.运行时动态链接:

​ 如果在运行之前,就把所有的都链接起来装入,就会造成浪费,因此是需要哪一个部分,在运行过程中就链接哪一个部分。在Windows中,需要有如下代码:

hLibrary=LoadLibrary(“c:\\test\\debug\\dbdll.dll”);
CallProc=(ALLPROC)GetProcAddress(hLibrary,“choose”);
res=(char *)(*CallProc)(tt3);

举例:一个程序如果需要100DLL,那么在运行过程中,不可能一次性都需要这100

  • 节省内存
  • 时间会变慢
  • 所以说,具体问题具体分析。如果内存大,就装入时动态;如果内存小,速度快,就运行时动态链接。

动态链接的优点:

  • 节省内存
  • DLL中如果函数改变,是不需要重新编译链接的
  • 不同开发工具可调用相同的DLL
  • 用运行时动态链接可以使得本次不用的函数不链接

二、连续分配存储管理方式

2.1 单一分配连续

​ 将内存分为两个区,用户区与系统区,程序可在用户区上跑。

  • 简单
  • 内存利用率低
  • CPU利用率低,需要等待I/O

2.2 固定分区分配

​ 将内存分成大小不定的若干分区,OS占一个区,剩下的每个作业可以占一个区。同时系统需要设定一个分区表,分配时从表中找一个符合的分配。

2.3 动态分区分配

​ 为了使分配的大小与作业的大小相同,将分配后剩下的内存组织起来,进行分配。

2.3.1 首次适应算法FF

​ 从分区表的首部依次查找满足的分区,然后分配给作业。

  • 先利用低地址,可以省下高地址
  • 低地址留下碎片,而且每次找低地址,会查找变慢
2.3.2 循环首次适应算法

​ 在FF的基础上,从下一个分区往下找。但是如果找到一个大空间之后,来了一个小作业,就会被占下一个更大的空间。

  • 使内存中空闲区域均匀,节省查找时间
  • 会缺乏空闲的大空间
2.3.3 最佳适应算法

​ 前两种,是按照低地址到高地址排序,而这个算法是按照空闲区的大小由小到大排序。要注意这个区别!

  • 会留下许多小碎片
2.3.4 分配流程

在这里插入图片描述

2.3.5 回收

回收有四种情况,很简单,看看图即可。
在这里插入图片描述

2.3.6 动态重定位分区分配

​ 将剩下的小碎片拼接起来,分配给作业,但是要解决重定位的问题。在拼接的时候,是将分配过的挪动,最终出现一个连续的空闲内存。
在这里插入图片描述

2.3.7 对换

​ 对换的意思其实与挂起差不多的。

总结:

​ 以上方式都想尽了一切办法减少资源的浪费,但是连续分配存在着致命的缺点,因此在现在的计算机中,都采用离散的方式进行分配。

三、分页存储管理方式

3.1 基本概念

页/页面: 将程序按照一定大小,划分成若干个大小相等的页/页面

块/页框: 相应地,把内存划分成等大小的块/页框

页内碎片: 在装入的时候,最后一片经常会造成小碎片

页/块设置过大: 页内碎片过大

页/块设置过小: 页表的大小会比较大,占据内存,同时降低了页面换进换出的效率。

页表: 是描述页面与页框的一个映射关系的数据结构

地址结构: 将地址分成了两个部分,前部分是页号,后一部分是页内偏移

对于给定地址划分如下:
在这里插入图片描述

  • 内存中最多有多少个页:

    • 2^20
  • 页的大小:

    • 2^12
  • 两个进程的页大小可以不一样吗:

    • 显然是不可能的

3.2 地址变换结构

3.2.1 最基本的变换结构

在这里插入图片描述

  • 首先从页表寄存器中,获取页表的起始地址
  • 然后根据页号与页表起始地址,去找到块号
  • 根据块号与偏移量,计算出真实的地址

以上地址转换,要读几次内存?

Answer:两次,第一次是读页表,另一次是读真正的内存。

3.2.2 通过Cache的思想,引入快表

为什么可以这么做?

Answer:局部性原理。

因此当我们地址转换时,首先查快表,如果快表中没有查到,那么再去查页表

3.3 两级与多级页表

页表可以不可以放到离散的空间?

Answer:根据转换机制,单纯地相加去查块号导致页表是不能离散的。

页表过大怎么办?

Answer:采用多级页表的方式,从而做到页表可以离散地分到内存中。经典的划分为二级页表:
在这里插入图片描述
此时访问内存需要多少次?

Answer:一共需要三次。

四、分段存储管理系统

4.1 分段的引入

只采用分页的思想,会有什么缺点?

Answer:

  • 不利于模块化编程
  • 不利于分段共享
  • 不利于保护
  • 不利于动态链接
  • 不利于动态生长,在分段中可以进行扩充,而分页不容易
  • 归根到底,跟用户编程的风格不一样

4.2 分段基本原理

类似于分页,地址分为段号与段内地址。同时还要设计一个段表,但不同的是,会多一个段的大小。

为什么要有段长呢?

Answer:很显然的一点是因为分段是不固定大小的。

地址转换:
在这里插入图片描述

  • 首先检查段号是否越界,如果不越界,进行下一步
  • 检查段内地址是否越界,如果不越界,进行下一步
  • 将基址与段内地址相加,即可得到。
  • 那么需要几次访问内存?2次。
  • 因为访问要两次,那么如何解决这个问题?快表!
  • 快表的道理是什么?程序局部性原理

4.3 分页与分段的区别

  • 页是信息的物理单位,对用户是透明的,由系统决定;而段是由用户控制的,例如说程序段、数据段、代码段
  • 页的大小固定而段的大小不固定
  • 分页是一维的,而分段是二维的

4.4 信息共享

为什么分段可以很容易地实现共享?

Answer:因为页是非常碎的,如果将分页做成共享的话,会有许多的指针,而分段的大小会相对来说大一点,使共享更容易实现。

4.5 段页式存储思想

为了结合分段与分页的优点,可以将分段与分页相结合,因此修改成如下的地址:
在这里插入图片描述
那么地址的转换顺序也将改变:
在这里插入图片描述
同理,该方法也需要访问内存三次。

以上分页与分段式的方法,在《计算机组成原理》中都学到过,不难,计算起来仔细点即可!

五、虚拟内存空间

5.1 虚存的引入

1.一个非常非常经典的问题,我的电脑只有512MB,但是我就是很贪,我想玩GTA 5,那么请问我可以玩这个游戏吗?

Answer:

  • 你在想屁吃,重买电脑可以解决问题
  • 当然可以,就是慢了
  • 因为在运行游戏的时候,我也不可能是所有的代码都在运行,因此我每次装入一部分到内存中,就可以了(虽然这样很慢,很没有游戏体验)

2.继续上一个问题,如果装入的过程中,满了怎么办?

Answer:

需要把一些不用的页给它置换出去。

3.这样做的依据是什么?

Answer

程序的局部性原理可以说也用到了很多。

  • 时间局部性
  • 空间局部性

虚存的定义:

可以使用户在一块很大的空间中编程,将内存与外存相结合。

5.2 虚存的实现

1.连续分配的内存可以实现虚存吗?

Answer:不可以,因为在连续分配是一次性将作业全部装入内存之中,这显然是不可以的

5.2.1 请求分页机制
  • 请求分页机制
  • 缺页中断
  • 地址变换结构

页表机制:
在这里插入图片描述

  • 状态位:代表该页是否在内存还是外存
  • 访问字段:记录本页在一段时间内访问次数
  • 修改位:代表该页在内存中是否被访问过(为了写回外存)

请求分页的流程:
在这里插入图片描述
缺页中断与一般中断的区别:

  • 缺页中断可以在指令执行间得到服务
  • 一条指令可造成多次缺页中断
5.2.2 请求分段机制
  • 请求分段机制
  • 缺段中断
  • 地址变换结构

请求分段流程:
在这里插入图片描述
地址变换过程:
在这里插入图片描述

5.2.3 分段的共享与保护

共享段表:
在这里插入图片描述

  • 共享进程计数器:记录有多少个进程在共享
  • 存取控制字段:访问控制(只读、只写这种)
  • 段号:不同进程可以使用不同的段号表示一个共享段

共享段的分配:

  • 对第一个请求,调入段区
  • 对其他请求,不需再调入内存,直接分配

共享段的回收:

  • 如果COUNT != 1,那么直接撤销即可
  • 如果COUNT == 1,那么要回收该段

5.3 页面置换算法

抖动: 刚被置换出的页面,又要被访问,那么需要重新调入,频繁地换进换出,会造成大量时间上的浪费。因此如何置换,是一个非常重要的事情。

置换原则:

  • 换出页面再访问时间越长越好
  • 修改过的页面代价高于未修改过的(是因为要重写修改过的页)
5.3.1 最佳算法

可以说是最尴尬的算法,因为不知道后续的作业顺序,所以仅停留在理论角度。该算法的核心思想为;

将物理块中最长时间不访问的页面置换出去。

5.3.2 先进先出算法FIFO

核心思想:淘汰掉在内存中待的时间最长的页面。

  • 适合于按顺序访问
  • 会淘汰掉频繁重复被访问的页面
5.3.3 最近最久未使用LRU

核心思想:淘汰掉最近最久未使用过的页面。

硬件实现:

  • 寄存器:移位寄存器,定期右移,定期置1。调出最小的就可以。
  • 栈:栈顶始终是最近刚访问的
5.3.4 CLOCK算法

该算法类似于LRU算法,基本上不会考到。
同时还有一个改进的CLOCK,个人感觉也不会考到。

5.3.5 最近最少使用LFU

也是LRU的变种,采用一个计数,统计在一段时间内使用次数最少的页面。一定要把这个和LRU区别开!!!
在页面置换算法中,页面中断与页面置换是有区别的!计算起来一定要注意!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值