内存由很多小的存储单元组成,每个存储单元对应一个内存地址。
如果计算机按字节编址,则一个内存地址对应的存储单元存储一个字节。
如果计算机的字长是32位,则每个内存地址对应的存储单元存储一个字,每个字的大小为32个二进制位。
指令是由操作码和若干参数组成的。
装入模块在进行装入的时候,有三种方式:绝对装入,静态重定位,动态重定位。
绝对装入;在编译时,直接将逻辑地址转化为绝对地址。在装入的时候,装入程序直接按照装入模块中的绝对地址
进行装入。
静态重定位:又称可重定位式装入。它是在装入的时候才进行逻辑地址和绝对地址之间的转化。静态重定位有一个缺陷,在一个作业进行装入的时候必须为其分配全部的内存空间,否则无法装入。
动态重定位:又称动态运行时装入。它解决了静态重定位的缺陷,只有在程序需要执行的时候才进行装入,并且允许程序在内存中发生移动,这种方式需要一个重定位寄存器的支持。
链接的三种方式:静态链接、装入式动态链接、运行时动态链接。
静态链接:在装入前就链接好。
装入时动态链接:在装入的时候,边装入边链接。
运行时动态链接:在程序执行到这个模块的时候,再进行链接。
方式1:通过上下限寄存器,进行判断。通过这两个寄存器就可以判断是否越界,然后决定是否允许访问。
方式2:通过重定位寄存器和界地址寄存器。重定位寄存器存储的是存储单元的起始地址,界地址寄存器存储的是最大逻辑地址。通过这两个寄存器就可以判断是否越界,然后决定是否允许访问。
内存管理主要包含四个模块:内存的分配和回收、内存空间的扩充(覆盖、交换、虚拟存储技术)、地址转换、存储保护。
在本节主要讲解虚拟内存以外的其他技术,虚拟内存技术将会另起一个章节进行讲解。
覆盖技术:将程序分为多个段。常用的段常驻内存,不常用的段在需要时调入内存。
常驻内存的段放在固定区,不常用的段放在覆盖区,需要用到时调入内存,不需要的时候换出外存。
运用覆盖技术的话,覆盖结构必须由程序员声明,并且对用户不透明,增加了编程负担。
内存空间紧张的时候将程序中的某些进程暂时换出外存,把外存中某些具备运行条件的进程换入内存。
中级调度就是在将某个处于挂起态的进程调入内存。因此中级调度又叫内存调度。
处于就绪状态和阻塞状态的进程都有可能被挂起。当被激活,就会解除挂起状态。
内存空间的分配方式,可以分为连续分配和非连续分配管理方式两种。
连续分配管理方式:单一连续分配,固定分区分配,动态分区分配。
在单一连续分配方式中,内存被分为系统区和用户区。
缺陷:
优点:
将用户空间划分成若干个固定大小的分区,每个分区中装入一个作业。无外部碎片有内部碎片。
动态分区分配又称可变分区分配,不会虚线划分内存分区,而是在装入进程的时候,根据进程的大小动态的建立分区,使分区的大小刚好适合进程的需要。
因此系统分区的数目和大小是可变的。
动态分区分配没有内部碎片但是有外部碎片。
常见的四种动态分区分配算法:首次适应算法,最佳适应算法,最坏适应算法,临近适应算法。
空闲分区以地址递增的方式进行排列,从头到尾寻找合适的分区。
优点:综合看性能最好,算法开销小,回收分区后不需要对空闲分区队列进行重新排序。
优先使用空间更小的分区。
空闲分区以容量递增的方式进行排列。
优点:会有很多大分区被保留下来,更能满足大进程的需求。
缺点:会产生很对太小的,难以利用的碎片。并且算法的开销比较大,因为在回收分区后,需要对分区重新排序。
优先使用更大的分区。
空闲分区以容量递减的顺序进行排列。
优点:可以防止产生小的,不可用的碎片。
缺点:虽然解决了最佳适应算法的缺点,但是无法保留大分区,对大进程并不友好。
最坏适应算法其实是由首次适应算法演变过来的,每次从上次查找结束的位置开始查询。
空闲分区以地址递增的方式进行排序,并组成一个循环链表。
优点:不用每次都从最低的地址开始检索。算法开销小。
缺点:高地址的大分区也会被用完。
如果允许将一个进程分散地装入许多不相邻的分区中,便可充分的利用内存,苏旭在进行“紧凑”处理。
基于这一思想提出了离散分配管理方式。
离散分配管理方式主要有:分页存储管理、分段存储管理、段页式存储管理三种。
把进程分页,进程的各个页面可以离散的分散到各个内存块中。
分页存储管理方式是如何实现的:逻辑地址经过地址转换,得到页号和页内偏移量。根据页号查询页表(页表由页号和内存块号组成),找到最终需要访问的内存块。
需要注意:页表中的每个页表项的长度相同,页号是隐含的。分页管理方式中,地址是一维的。
单级页表存在的问题:
1.页表必须用一块连续的内存空间来进行存放。当页表很大的时候需要占用很多连续的页框,造成内存的浪费。
2.没有必要让整个巨大的页表持续的占用内存,这样会造成性能的浪费。
因此可以采用多级页表来解决这个问题。
我们这里以两级页表为例进行介绍:腰围离散的页表再建立一层页表,这层页表叫做页目录表,外层页表或者顶层页表。
一级页表的地址结构由两部分组成:页号和页内偏移量。
二级页表的地址结构由三部分组成:一级页号,二级页号,页内偏移量。
以下图为例:
将一个巨大的页表拆分成1024个小页表。这样我们在对某个地址进行访问时,首先需要根据一级页号查询顶级页表,根据顶级页表中的内存块号,查询该内存块,得到二级页表,二级页表在此时触发缺页中断,并将其被加载到内存中(如果采用虚拟存储技术,会在该页表项上加一个标志位,表示该页表目前是否处于内存总,如果已经在内存中,则无需再将该页表调入内存)。再根据二级页号查询二级页表,对查询到的内存块号进行访问。
注意:在分也是存储管理中,各级也标的大小不能超过一个页面。
在分段存储管理中:一个程序按逻辑结构可以分为多个段。
逻辑地址结构是由段号和段内地址组成。
实现逻辑:首先,通过逻辑地址,得到段号和段内地址,检查段号是否越界。然后去查询段表,得到该段号对应的基址和段长,并通过段长检查段内地址是否合法。在段表中,因为每个段表项的大小都是固定的,因此段号是隐含的,并不占存储空间。
分段存储的优点,更容易实现信息的共享和保护。
为什么要有段页式存储?
因为页式存储和段式存储各有优缺点:
段页式存储实现流程:
首先逻辑地址构成为段号加页号加页内地址。
段表中存储:段号、页表存放块号、页表长度。
页表中存放:页号、内存块号。
首先,操作系统会根据逻辑地址,判断段号是否合法,然后查询段表,根据段表中的信息,得到存储页表的块内地址、页表长度,从而得到页表。然后判断页号的合法性,如果合法,则根据页内地址和内存块号的访问目标存储单元。整个过程需要三次访存。
注意:分段对用户来说是可见的,而分页对用户来说是不可见的。如果引入快表结构,则在快表命中的时候只需要一次访存。
操作系统的交换技术和虚拟存储技术都是为了扩充内存的容量,但是它们有一些区别:
交换技术是在不同的进程(作业)间进行的,当内存紧张时,可以把暂时不能执行的进程换出到外存,腾出内存空间,再把需要执行的进程换入到内存。
虚拟存储技术是在一个进程(作业)内部进行的,当一个进程的大小超过了内存的容量时,可以把进程分成若干个页或段,只把当前需要用到的页或段装入到内存,其他的页或段放在外存,从而实现逻辑上的内存扩充。
举个例子,玩游戏时,虚拟存储技术可以让我们只把当前场景的数据加载到内存,而不需要把整个游戏的数据都放在内存里。而交换技术可以让我们在后台运行多个程序,当我们切换程序时,可以把不用的程序换出到外存,再把要用的程序换入到内存。
当操作系统的缺页率高的时候,就会换出一些进程。当缺页率明显降低,则停止换出。
外存分为文件区和对换区。文件区属于离散分配,访问的时候属于随机IO,注重空间利用率。对换区是连续分配,在访问的时候属于连续IO,注重效率。
因此当换出的时候,会被换出到对换区。
可以换出优先级低的进程,也可以优先换出阻塞进程,因为需要防止出现内存抖动现象(刚换入内存的进程被快速换出)操作系统会考虑进程的驻留时间。
传统存储管理方式存在一些缺点:尤其是一次性和驻留性。
一次性是指:作业数据必须一次全部调入内存中。
驻留性是指:作业数据在整个运行期间都会常驻内存。
虚拟内存技术是基于局部性原理提出的,要改善传统存储方式的缺点首先要了解局部性原理。
局部性原理:包含时间局部性、空间局部性、顺序局部性。
时间局部性:如果一个信息项正在被访问,那么在近期还有可能被访问。
空间局部性:如果一个信息正在被访问,那么将来被访问的信息项在空间地址上很可能和它是临近的。
顺序局部性:大部分指令都是顺序执行的。顺序执行和非顺序执行的比例大致是5比1。
指令的顺序执行和数组的连续存放是产生顺序局部性的原因。
定义:程序不需要完全装入即可运行,运行时根据需要动态装入,若内存不够,还需要换出一些数据。
特征:多次性、对换性、虚拟性
访问的信息不存在于内存中时,由操作系统将所需要的信息从外存调入内存(请求调页)。
当内存空间不够的时候,将内存中暂时用不到的信息换入到外存。(页面置换)
虚拟内存的实现:依赖于请求分页、请求分段和请求段页式存储管理技术。
下图为请求分页存储管理技术的页表:
它比普通的分页存储的页表多了四个字段:状态位、访问字段、修改位、外存地址。并且引入了缺页中断机构。
举个例子,需要访问一个地址,我们根据逻辑地址来计算出它所对应的页号,判断页号是否越界,如果页号是合法的,那么接下来我们需要查询快表,如果快表没有命中,则需要查询页表(慢表)。在查询页表时,首先查询状态位,通过状态位得知该页号是否已经在内存中,如果不在内存中则触发缺页中断机构,将当前缺页的进程阻塞,放入阻塞队列,进行调页,调页完成后再将其唤醒,再放入就绪队列。
在调页的时候,如果内存中有空闲块,则将所缺页面装入空闲块。如果没有空闲块,则依据访问字段的值(访问字段可以在一定程度上表明该页面的访问频率)通过页面置换算法淘汰掉一个页面,放入外存。如果通过修改位得知,被淘汰的页面在内存中发生过修改,则需要根据外存地址将其重新写入外存。未被修改过的页面在被页面置换算法淘汰时,无需写回外存。页面被调入回内存后,需要将该页面写入快表中。
注:缺页中断是由于当前执行的指令产生的,因此属于内中断。
OPT(optimal)算法,又称最佳置换算法,预知接下来的一个时间段内访问频率最低的那个页面,并将其淘汰,这种算法性能好,但是无法实现。
先进先出算法,根据页面进入内存的先后顺序,淘汰最早进入内存的页面。
因为在淘汰的时候完全不考虑内存中页面的使用频率,也就导致,增加内存块的数量并不一定增加命中率,故存在贝拉迪异常。
淘汰掉最近的一个时间段内使用频率最低的那个页面,留下最近访问频率最高的页面。
算法简单,性能也不错。
访问位为1代表最近访问过,访问位位0代表最近未访问过。
对访问位进行循环扫描,并判断是否为0,如果为1则置0,对最先扫描到的访问位为0的页面进行淘汰。
若在第一轮扫描中未扫描出访问位为0的页面,则在第二轮中进行淘汰。
考虑到了修改位,淘汰规则如下:
页面分配有固定分配和可变分配两种:
置换策略有局部置换和全局置换两种:
常用的页面分配与置换策略有三种:固定分配局部替换,可变分配全局替换,可变分配局部替换。
固定分配局部替换:首先,一个进程在运行时所占用的内存块数量是固定的。其次,当发生缺页时只能在该进程的内存的页面中选出一页进行换出。
可变分配全局替换:刚开始会为每个进程分配一定数量的内存块,这个数量在后续的操作中是可变的。操作系统会维护一个空闲块队列,当前进程发生缺页的时候,若还有空闲块,则会从空闲块中取出一块分配给该进程。若已经没有空闲块,则从其他进程的内存中选择一个未锁定的页面通过页面置换算法换出外存。这种方法会导致被选中的进程的物理块会减少,缺页率会增加。
可变分配局部置换:刚开始会为每个进程分配一定数量的内存块,这个数量在后续是可变的。当前进程发生缺页的时候,只允许从该进程自己的内存块中通过页面置换算法选出一个页面换入外存中。这种方式的优点在于,可以通过该进程缺页率的大小,来动态的调整该进程的内存块数量。当这个进程频繁缺页,缺页率特别高,则应该适当增加为该进程分配的内存块数量。当这个进程几乎不会发生缺页,则应该适当降低其所占用的内存块数量。
常见的调用时机有两种,预调页和请求调页。
请求调页:当发生缺页的时候,才将所缺页面调入内存。这种策略调入的页面一定会被访问到,但是每次只能调入一页,每次调入都需要IO操作,会导致频繁IO。
预调页:根据局部性原理可知,如果一个页面被被访问,那么与之相邻的页面也有很大的几率被访问,将相邻的页面预先调入内存,这样可以减少IO次数,防止出现频繁IO。但是目前预测的成功率在百分之50左右,故这种策略目前多用于首次调入。
外存分为对换区和文件区。文件区采用离散分配的方式,对换区采用连续分配的方式,速度更快。
UNIX在运行前,将文件进程相关的数据全部放在文件区,故未使用过的页面都可以从文件区调入。若被使用过的页面需要换出,则换出到对换区。
颠簸是指: