内存的基础知识
计算机的数据和指令都是装在外存中的,由于cpu与外存数据读取数据速度有差异,因此要把运行的程序放在内存中方便读取。那么从写程序到运行程序有几个步骤呢?
- 编辑源代码文件
- 编译:由源代码生成目标模块,也就是高级语言翻译为目标语言
- 链接:由目标模块生成装入模块,链接后形成完整的逻辑地址
- 静态链接:装入前链接形成一个完整装入模块
- 装入时动态链接:运行时边装入边链接
- 运行时动态链接:运行时需要目标模块才装入并链接
- 装入:将装入模块装入内存,装入后形成物理地址
- 绝对装入:编译时产生绝对地址 (单道程序阶段
- 可重定位装入:装入时将逻辑地址转换为物理地址 (早期的多道批处理操作系统
- 动态运行时装入:运行时将逻辑地址转换为物理地址,需设置重定位寄存器 (现代操作系统
另外,计算机中可能同时运行着许多进程,每个进程都涉及到一段内存,操作系统在其中需要发挥什么作用呢?首先是内存空间的分配与回收和内存空间的扩充,这段我们在下面的学习中会涉及到。其次是从逻辑地址到物理地址的转换,也就是装入。还有就是存储保护,因为要保证各进程在自己的内存空间内运行,不会越界访问。有两种方式来实现存储保护:
- 设置上下限寄存器:有两个寄存器分别存储该进程的绝对地址的上下界,访问时先判断要访问的存储单元是否在上下界之间。
- 利用重定位寄存器、界地址寄存器进行判断:重定位寄存器中存放该进程的起始物理地址,界地址寄存器存储该进程的最大逻辑地址,访问时先判断相对地址是否小于界地址寄存器,若小于再加上重定位寄存器得到绝对地址来进行访问。也称为基址寄存器和限长寄存器
内存的分配与回收
内存空间的分配与回收分为连续分配管理方式和非连续分配管理方式
连续分配管理方式
连续分配分为三种,在具体说明之前,我们先来区分两个概念:内部碎片和外部碎片。内部碎片指分配给某进程的内存区域中,有些部分没有用上;外部碎片指的是内存中某些空闲分区由于太小而难以利用,可以通过拼凑技术来解决外部碎片。
- 单一连续分配:内存被分为系统区和用户区,内存中只能有一道用户程序,用户进程独占整个用户区空间。
- 优点:实现简单无外部碎片,不一定采取内存保护
- 缺点:只能用于单用户单任务的操作系统当中,有内部碎片,储存器利用率极低
- 固定分区分配:将用户空间划分为若干个固定大小的分区,在每个分区内只装入一道作业,分区大小可以相等也可以不等。建立分区说明表来实现各个分区的分配与回收,每个表项包含对应分区的大小、起始地址、状态
- 相等:缺乏灵活性,但是适用于一台计算器控制多个相同对象的场合
- 不等:增加灵活性,可以满足不同大小的进程需求
- 优点:实现简单无外部碎片
- 缺点:用户程序太大时可能导致覆盖,会产生内部碎片,空间利用率低
- 动态分区分配:不会预先划分内存分区,而是根据进程的大小动态地建立分区。采用空闲分区表或空闲分区链来记录内存使用情况
- 没有内部碎片,但有外部碎片
- 在回收时,相邻的空闲分区需要合并
动态分区分配算法
这是动态分区分配的重要部分,有四种算法
- 首次适应算法:从低地址开始查找,找到第一个能满足大小的空闲分区。空闲分区以地址递增的次序排列,每次分配内存时查找空闲分区链或空闲分区表。算法开销小,综合性能最好
- 最佳适应算法:优先使用更小的空闲区。空闲分区按容量递增次序链接,每次分配内存时查找空闲分区链或空闲分区表,找到大小能满足要求的第一个空闲分区。会产生很多的外部碎片
- 最坏适应算法:与最佳使用算法相反。空闲分区按容量递减次序链接,需要每次查找后都更新空闲分区链,算法开销大。可能会使大进程没有内存分区可用
- 临近适应算法:由首次使用演变而来,空闲分区以地址递增的顺序排列,每次分配从上次查找结束的位置开始查找空闲分区链或空闲分区表。算法开销小
非连续分配管理方式
非连续分配分为三种,分别是页式存储管理、段式存储管理和段页式存储管理
基本页式存储管理
先来区分几个容易混淆的概念:
- 页框:将内存从物理上划分为一个个大小相等的分区,也叫做页帧、内存块、物理块、物理页面
- 页框号:每个页框有一个编号,从0开始。也叫做内存块号。
- 页:进程在逻辑上被划分成的一个个部分,也叫做页面
操作系统以页框为单位为各个进程分配内存空间,进程的每个页面分别放入一个页框中,也就是说进程的页面与内存的页框一一对应。为了能知道进程的每个页面在内存中存放的位置,操作系统为每个进程建立一张页表,记录进程页面和实际存放内存块之间的映射关系
- 一个进程对应一张页表
- 进程的每个页面对应一个页表项
- 每个页表项由页号和块号组成,由于页表项在内存中连续存放,所以页号可以是隐含的
将上面这些从大到小排列:进程(页表)-页号P(逻辑地址/页面长度)-页面(页框,页表项,页表项内部是块号b)-页内偏移量W(逻辑地址%页面长度)。也要区分以下几个长度:
- 页表长度:这个页表中共有几个页表项(页面、页)
- 页表项长度:每个页表项占多大的存储空间
- 页面大小L:一个页面占多大的存储空间
如何实现地址转换?这是计算题的出题重点
逻辑地址可拆分为页号P和页面偏移量W。先计算出逻辑地址,再找到页面在内存中的存放位置(查页表),最后计算物理地址=页面地址+页面偏移量
基本地址变换机构
这节更加详细地描述了借助进程的页表将逻辑地址转换为物理地址的过程,常出计算题
在操作系统中会设置一个页表寄存器,存放页表在内存中的页表起始地址F和页表长度M。进程未执行时它们被存放在进程控制块中,进程被调度时,操作系统会把他们放在页表寄存器中。
这节还是比较复杂,看看王道的总结吧
具有快表的地址变换机构
快表,又称联想寄存器TLB,是一种访问速度比内存快很多的高速缓存,用来存放最近访问的页表项的副本,可以加速地址变换的速度。与此对应,内存中的页表常称为慢表。
两种方式的对比:
两级页表
书上没有这段,先贴几张ppt在这
基本段式存储管理
按照程序自身的逻辑划分成若干段,编程更方便可读性更高。每个段都有一个段名,每段从0开始编址。内存以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。逻辑地址由段号和段内地址构成。段号可以是隐含的,不占存储空间。
与页和页表类似,段也是有段表的,为了从物理内存中找到各个逻辑段的存放位置,需要在进程建立一张段映射表,简称段表。每个段对应一个段表项,记录了该段在内存中的起始位置(基址)和段的长度。各个段表项的长度是相同的。
与分页管理不同的是,每段的大小是不同的,因此需要对段内偏移量和段长进行检查。具体的地址变换过程如下:
- 由逻辑地址得到段号、段内地址
- 段号与段表寄存器中的段长度比较,检查是否越界
- 由段表始址、段号找到对应段表项
- 根据段表中记录的段长,检查段内地址是否越界
- 由段表中的“基址+段内地址"得到最终的物理地址
- 访问目标单元
基本段页式存储管理
分页管理不会产生外部碎片,但不方便按照逻辑实现信息的共享和保护
分段管理方便按照逻辑实现信息的共享和保护,但可能会产生外部碎片
因此提出了段页式管理,将进程按照逻辑模块分段,再将各段分页,再将内存空间分为大小相同的内存块/页框/页帧/物理块。一个进程对应一个段表,但可能对应多个页表。
地址变换过程如下:
好复杂,看看王道的总结吧
内存空间的扩充
覆盖与交换
这段不是考察重点,只会在选择题中出现
虚拟存储技术
传统的非连续分配存储管理分为基本分页存储管理、基本分段存储管理、基本段页式存储管理,它们有两个特点:一次性:作业数据必须一次全部调入内存;驻留性:作业数据在整个运行期间都会常驻内存。可能会导致大作业无法运行、多道程序并发性下降、内存资源浪费等问题。
局部性原理:
- 时间局部性:现在访问的指令、数据在不久后很可能会被再次访问
- 空间局部性:现在访问的内存单元周围的内存空间,很可能在不久后会被访问
- 高速缓存技术:使用频繁的数据放到更高速的存储器中
为了解决这些问题,提出了虚拟存储技术。也就是允许一个作业分多次调入内存(建立在离散分配的内存管理方式基础上),程序不需要全部装入即可运行,运行时根据需要动态调调入数据,若内存不够,还需要换出一些数据。有几个特性:
- 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存
- 对换性:无需在作业运行时一直常驻内存,而是允许在作业运行过程中,将作业换入、换出。
- 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量。
有两个主要功能:
- 请求调页功能:访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存
- 页面置换功能:内存空间不够时,将内存中暂时用不到的信息换出到外存
那么如何实现虚拟存储技术呢?有三种实现方法:
- 请求分页存储管理
- 请求分段存储管理
- 请求段页式存储管理
请求分页存储管理
页面置换算法
- 最佳置换算法:每次选择淘汰以后用不使用或最长时间不再被访问到的页面,以保证最低的缺页率。缺页率最小性能最好,只是一种理想化的算法,在实际应用中是无法实现的。
- 先进先出置换算法FIFO:每次选择淘汰最早进入内存的页面。实现简单但算法性能差,内存块增多缺页次数反而增加的情况称为belady异常。
- 最近最久未使用置换算法LRU:淘汰最久未使用的页面,用访问字段记录该页面自上次访问以来所经经历的时间t。做题时逆向扫描最后一个出现的页号。算法性能好但实现困难开销大。
- 时钟置换算法CLOCK:又称为最近未用算法。实现简单算法开销小,但未考虑页面是否被修改过
- 改进型时钟置换算法:其他条件相同时,优先淘汰没有被修改过的页面。算法开销较小,性能也不错