为什么要进行内存管理?
页式管理中每个页面表象大小的下限如何决定?
多级页表解决了什么问题?又会带来什么问题?
内存管理的基本原理和要求
操作系统对内存的划分和动态分配,就是内存管理的概念。内存管理的功能
程序装入和链接
将用户源程序变为可在内存中执行的程序,通常需要以下几个步骤:
编译。由编译程序将用户源代码(.c)编译成若干目标模块(.o )–>翻译成机器语言或汇编语言
链接。由链接程序将编译后形成的一组目标模块及所需的库函数链接在一起,形成一个完成的装入模块 --> 也就是形成完整的装入模块
装入。有装入程序将装入模块装入内存内运行
一些名次的解释
程序的链接有三种:静态链接、装入时动态链接、运行时动态链接
静态链接
在程序运行之前,先将各目标模块及它们所需的库函数链接成一个完整的可以执行的程序,以后不再拆开(也就是说,链接完再装入模块)
我们通过一个例子来说明在实现静态链接时应解决的一些问题。在图a中示出了经过编译后所得到的三个目标模块A、B、C,它们的长度分别为 L、M和N。在模块A中有一条语句CALL B,用于调用模块B。在模块B中有一条语句CALL C,用于调用模块C。B和C都属于外部调用符号,在将这几个目标模块装配成一个装入模块时,须解决以下两个问题:
(1) 对相对地址进行修改。在由编译程序所产生的所有目标模块中,使用的都是相对地址,其起始地址都为 0,每个模块中的地址都是相对于起始地址计算的。
在链接成一个装入模块后,原模块B和 C在装入模块的起始地址不再是 0,而分别是 L和 L+M,所以此时须修改模块B和C中的相对地址,即把原B中的所有相对地址都加上 L,把原 C中的所有相对地址都加上L+M。
(2) 变换外部调用符号。将每个模块中所用的外部调用符号也都变换为相对地址,如把B 的起始地址变换为 L,把 C 的起始地址变换为 L+M,如图 b所示。
这种先进行链接所形成的一个完整的装入模块,又称为可执行文件。通常都不再拆开它,要运行时可直接将它装入内存。这种事先进行链接,以后不再拆开的链接方式,称为静态链接方式。
装入时动态链接
装入时动态链接。将用户源程序编译后得到一组目标模块,在装入内存时,采用边装入边链接的方式(边装入边链接)
用户源程序经编译后所得的目标模块,是在装入内存时边装入边链接的,即在装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将它装入内存,还要按照图4-4所示的方式来修改目标模块中的相对地址。装入时动态链接方式有以下优点:
(1) 、 便于修改和更新。对于经静态链接装配在一起的装入模块,如果要修改或更新其中的某个目标模块,则要求重新打开装入模块。这不仅是低效的,而且有时是不可能的。若采用动态链接方式,由于各目标模块是分开存放的,所以要修改或更新各目标模块是件非常容易的事。
(2)、 便于实现对目标模块的共享。在采用静态链接方式时,每个应用模块都必须含有其目标模块的拷贝,无法实现对目标模块的共享。但采用装入时动态链接方式,OS则很容易将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。
运行时动态链接
运行时动态链接。对某些目标模块的链接,是在程序中需要该目标模块时才进行的。其优点是便于修改和更新,便于实现对目标模块的共享(先装入,使用时再链接)
装入模块在装入内存时,有三种:绝对装入、可重定位装入、动态运行装入
绝对装入
在编译的时候,如果知道程序将驻留在内存的某个位置,则编译程序将产生绝对地址的目标代码。
绝对装入程序按照装入模块中的地址,将程序和数据装入内存
由于程序中的逻辑地址与实际地址完全相同,故不需对程序和数据的地址进行修改
绝对装入的特点:
可重定位装入
静态重定位的特点是
动态运行装入
程序在内存中若发生移动,则需要采用动态的装入方式
装入程序把装入模块装入内存之后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行
在装入内存后的所有地址均为相对地址
这种方式需要一个重定位寄存器的支持
动态运行装入的特点是
逻辑地址空间与物理地址空间
内存保护
内存保护的两种方法是
在CPU上设置一对上、下限寄存器,存放用户作业在主存中的下限和上限地址,每当CPU要访问一个地址时,分别和两个寄存器的值比较,判断有无越界
采用重定位寄存器(也就是基址寄存器)和界地址寄存器(也就是限长寄存器)来实现这种保护。
内存保护中注意的点。
覆盖
覆盖用来解决程序大小超过物理内存总和的情况
覆盖的基本思路是
覆盖的技术特点是
覆盖的缺点。
交换
交换的基本思想
交换要注意的问题
交换的技术特点
连续分配管理方式
连续分配方式是指为一个用户程序分配一个连续的内存空间
连续分配管理方式主要有
单一连续分配
单一连续分配是什么
单一连续分配的优点
单一连续分配的缺点
固定分区分配
固定分区是什么
固定分区划分的不同办法是
固定分区划分的过程
固定分区存在的问题
固定分区的优缺点
动态分区分配
动态分区是什么
动态分区的缺点和解决办法
动态分区的算法
首次适应算法
空闲分区以地址递增的次序链接。分配内存是顺序查找,找到第一个大小满足的空闲分区
最佳适应算法
最坏适应算法
临近适应算法
非连续性分配管理方式
非连续性分配管理方法根据分区的大小是否固定,分为分页存储管理方式和分段存储管理方式
基本分页存储管理方式
固定分区会产生内部碎片,动态分区会产生外部碎片,这两种技术对内存利用率都比较低
引入【分页】思想尽量避免碎片的产生:把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位
每个进程也以块作为单位进行划分,进程执行时,以块为基本单位,逐个申请主存中的块空间
【分页】的方法从形式上来看,像分区相等的【固定分区】技术,分页管理不会产生外部碎片
但它又有本质的区别
上述这样做的原因是,进程只会在为最后一个不完整的块申请一个主存块空间时,才产生主存碎片,所以尽管会产生内部碎片,内部碎片相对于进程也是很小的。
分页存储的几个基本概念
页面和页面大小。进程中的块称为页,内存中的块称为页框,外存中的块直接被称为块。
【注意】进程在执行时需要申请主存空间,也就是要为每个页面分配主存中的可用页框,这就产生了页和页框的一一对应。
为了方便转换,页面的大小一般来说是2的整数幂,,同时页面大小应该适中。
地址结构。页号+业内偏移量
比如,地址长32位,其中0~11位为页内地址,即每页4KB;12~31位为页号,地址空间最多允许2^{20}页
地址结构决定了虚拟内存的寻址空间有多大
页表。为了便于在内存中找到进程的每个页面所对应的的物理块,操作系统会为每个进程建立一张页表,它记录了页面在内存中对应的物理块号,页表一般存放在内存中。
基本地址变换机构
什么是基本地址变换机构?基本地址变换机构是将逻辑地址转换为内存中的物理地址
从逻辑地址到物理地址映射过程
基本地址变换机构的特点是要访问两次主存:第一次,是访问主存的页表,确定所取得数据数据或指令的物理地址;第二次是根据该地址去内存中存取数据或指令。
具有快表的地址变换机构
【具有快表的地址变换机构】其实是对【基本地址变换机构】的优化。正如上面介绍基本地址变换机构的特点,需要【访存两次】,这样执行速度就慢下来。于是在地址变换机构中增设一个具有并行查找能力的高速缓冲存储器——【快表(TLB)】,又称【相联存储器】,是通过内【容查】找的存储器(如果学过计算机组成原理的小伙伴一定还记得虚拟存储器中的快表吧),用来存放当前访问的若干页表项,以加速地址变换过程,与此对应的是主存中的页表,被称为【慢表】。
在具有快表的分页机制中,地址的变换过程如下:
两级页表:一级页号+二级页号+页内偏移
以32位逻辑地址空间、页面大小4KB、页表项大小4B为例,若要实现进程对全部逻辑地址的映射,则每个进程需要2^20个页表项,也就是说,仅仅保存页表项就需要4MB的存储空间,这显然是不合适的。
为了压缩页表,计算机科学家进一步延伸页表映射思想,就得到了二级分页。为了查询方便,顶级页表最多只能有一个页面,因此顶级页表总共可以容纳4KB/4B=1K个页表项。注意,1K个页表项占用的地址位数是10位,页面带奥4KB也就是12位,因此一个32位的逻辑地址空间就剩下了10位,正好使得二级页表的大小在一页之内。
如何解决单级页表问题?
问题一:页表必须连续存放,因此,当页表很大时,需要占用很多个连续的页框,违背了离散原则
问题二:没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定页面
解决方法:可将长长的页表分组,使每个内存块刚好可以放入一个分组。另外,要为离散分配的页表再建立一张页表,称为页目录表。
基本分段存储管理方式
分段方式主要目的是更好地满足用户需要,一个段通常包含着一组属于一个逻辑模块的信息,分段对用户是可见的,用户编程时需要显式的写出段名。
一些名词的理解
(1)分段:段号S+段内偏移量W
段式管理方式按照用户进程中的自然段话分逻辑结构的,其逻辑地址由段号S与段内偏移量W两部分组成。段号的长度代表着一个作业最多有多少个段;段内偏移量决定最大段的长度。与页式系统不同的是,在夜市地址中,逻辑地址的页号和页内偏移量对用户是透明的,但在段式系统中,段号和段内偏移量必须由用户显式提供。
(2)段表:段号+段长+本段在主存中的起始地址
每个进程都有一张逻辑空间和内存空间映射的段表,其中,每个段表对应进程的一段,段表项记录该段在内存中的始址和长度。
(3)地址变换机构
(4)段的共享与保护
分段管理的保护方法主要有两种:一种是存取控制保护,另一种是地址越界保护。
地址越界保护将段表寄存器中的段表长度与逻辑地址中的段号比较,若段号大于段表长度,则产生越界中断;再将段表项中的段长和逻辑地址中的段内位移进行比较,若段内位移大于段长,也会产生越界中断。(页内偏移是不可能越界的)
分段、分页管理的对比
页是信息的物理单位,分页的主要目的是为了实现离散分配,提高利用率。分页仅仅是系统管理上的需要,完全是系统行为,对用户是不可见的;段是信息的逻辑单位,分段的主要目的是更好地满足用户需要,一个段通常包含着一组属于一个逻辑模块的信息,分段对用户是可见的,用户编程时需要显式的写出段名。
页的大小固定且由系统确定;段段长度不固定,由用户编写的程序决定
分页的用户进程地址空间是一维的,程序员只需要给出一个记忆符即可表示一个地址;分段的用户进程空间是二维的,程序员在标识一个地址时,既要给出段名,又要给出段内地址
分段比分页更容易实现信息的共享与保护。不能被修改的代码称为纯代码或可重入代码(不属于临界资源),这样的代码是可以共享的;可修改代码不可共享。
访问一个逻辑地址需要几次访存。
多页(单级页表):第一次访存查找内存中的页表;第二次访问目标单元;分段:第一次访问查找内存中的段表;第二次访存目标内存单元
与分页系统类似,分段系统中也可以引入快表机构,将近期访问过的段表项放到快表中,这样可以有一定几率少访问一次主存,加快地址变换速度。
段页式管理方式
在段页式管理系统中,作业多地址空间首先被分成若干逻辑段,每个段都有自己的段号,然后将每个段分成若干个大小固定的页。
在段页式管理系统中,作业的逻辑地址分为三部分:段号S+页号P+页内偏移量。为了实现地址变换,系统为每个进程建立一张段表,每个分段有一张页表。段表表项中至少有段号S,页表长度和页表始址;页表表项中至少包括页号和块号,此外,系统中还应有一个段表寄存器,指出作业的段表始址和段表长度。在一个进程中,段表只能有一个,而页表可以有很多个,但每个段表只有一张页表。
段页式管理方式中,每个进程只有1张段表,每个段表只有1张页表。
【段页式系统的地址变换机构】
虚拟内存是基于"局部性原理"实现的,而局部性原理主要体现在两个方面:
【时间局部性】:程序中的某一条指令一旦执行,不久后该指令可能会再次执行;某数据被访问过,不久后可能再次被访问。产生时间局部性的原因是程序中存在着【大量循环】操作。【时间局部性】通过近来使用的数据和指令保存到【高速缓存存储器】中,并使用【高速缓存的层次结构】实现。
【空间局部性】:一旦程序访问了某个存储单元,在不久后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定范围内,因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。【空间局部性】通常使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。虚拟内存技术实际上建立了"内存-外存"的两级存储器结构,利用局部性原理实现高速缓存。
虚拟存储器的定义和特征
基于局部性原理,在程序装入时,将程序的一部分装入内存,而将其余部分留在外存,就可启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入主存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要掉入内存的信息。这样,系统好像为用户提供了一个比实际内存大得多的存储器,称为虚拟存储器。
很显然的,虚拟存储器实际上并不存在(现实中存在的仅仅只是4G或8G的主存储),但是由于系统提供了部分装入、请求调入和置换功能后,给用户好像有这么个存储器的感觉。
虚拟存储器具有三个特点:
虚拟存储器容量大小不受内存和外存限制,和CPU寻址位数有关
虚拟内存技术的实现
虚拟内存的实现需要建立在离散分配的内存管理的基础上。虚拟内存的实现有以下三种方式:
不管哪一种管理方式,都需要硬件支持。一般需要支持的有以下几个方面:
请求分页管理方式
【请求分页系统】建立在【基本分页系统】基础之上,为了支持虚拟存储器的功能而增加了【请求调页功能】和【页面置换功能】。请求分页功能是当前最常用的一种实现虚拟存储器的方法。为了实现【请求分页 】,除了需要 一定量的内存和外存的计算机系统,还需要有:页表机制、缺页中断机构和地址变换机构。
页表机制
由于使用局部性原理,页面分多次调入主存,因此在作业运行过程中,必然会出现要访问的页面啊不在内存的情况,如何发现和处理这种请求是【请求分页系统】必须要解决的,因此在【请求页表项】中增加了4个字段。
【基本分页系统】的页表项:页号+物理内存中的块号
【请求分页系统】的页表项:页号+物理内存中的块号+状态位P+访问字段A+修改位M+外存地址
状态位P。用于指示该页是否已调入内存,供程序访问时参考 。
访问字段A。用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问。供置换算法参考。
修改位M。表示该页在调入内存后是否被修改。供置换算法中【CLOCK置换算法】参考。
外存地址。用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考。
缺页中断机构
在请求分页系统中,每当要访问的页面不在内存中时,便会产生一个【缺页中断】,请求操作系统将所缺的页调入内存。【注意】此时应将缺页的进程阻塞(调页完成后唤醒)。若内存中有空闲块,则分配一个块给将要调入内存的那个块,并修改页表中的相应【页表项】;若此时内存中没有空闲块,则要淘汰某页。
缺页中断与一般中断比较类似,但也有以下两个明显的区别:
地址变换机构
【请求分页系统】中的【地址变换机构】是在【分页系统】的【地址变换机构】的基础上,为实现虚拟内存,又增加了某些功能而形成的。
在进行地址变换时,现检索快表TLB:
页面置换算法
OPT,最佳置换算法
OPT(Optimal),最佳置换算法选择的被淘汰的页面是以后永不使用的页面,或是在最长时间内不再被访问的页面,以便保证获得最低的缺页率。但我们没办法知道哪些页面是在未来最长时间不被访问的,所以【该算法无法实现】。
FIFO,先进先出算法
FIFO(First In First Out),先进先出算法有限淘汰最早进入的页面,即在内存中主流时间最久的页面。
FIFO算法会产生【所分配的物理块数增大而页故障数不减反增】的异常现象,被称为Belady异常。
LRU,最近最久未使用算法
LRU(Least Recently Used),最近最久未使用算法选择最近最长时间未访问过的页面予以淘汰。它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。
LRU算法的性能较好,但需要寄存器和栈的硬件支持。LRU算法是堆栈类算法。
CLOCK置换算法
CLOCK算法给每帧添加一个【使用位】。初始装入内存时,该页的使用位是1。当需要替换时,被扫到的页的帧1归0,当扫描时发现帧为0,则被淘汰。若全满,转一圈把所有帧都置0之后,再找第一个帧为0的页面被淘汰。
改进型CLOCK置换算法
设置两个位(使用位,修改位)。
淘汰顺序:(0,0)—(0,1)—(1,0)—(1,1)
以上淘汰顺序分表代表四种情况:
算法执行 如下操作步骤:
页面分配策略
驻留集是指给一个进程分配的物理页框的集合就是这个进程的驻留集。在设计留驻集需要考虑以下几点
驻留集大小
基于这些因素,现代操作系统通常采用三种策略
调入页面的时机
总结来说,【预调页策略】是运行前的调入,【请求调页策略】就是运行时的调入,
从何处调入页面
请求分页系统中的外存主要有两部分:用于存放文件的【文件区】和用于存放对换页面的【对换区】。
【文件区】通常采用离散分配方式;【对换区】通常采用连续分配方式,故对换区的I/O速度比文件区的更快。
从何处调入页面就有三种情况
抖动
【抖动】是指:刚刚换出的页面马上又要换入内存,刚刚换入内存的页面马上又要换出内存,发生频繁的页面调度。
【抖动】产生的【原因】是某个进程频繁访问的页面数目高于可用的物理页帧数目。
解决方法:撤销部分进程
工作集
【工作集】是指在某段时间间隔内,进程要访问的页面集合。存放的是最近被访问的n个页面。
根据工作集的大小,确定留驻集的大小。一般来说,留驻集的大小要大于工作集的大小,否则在运行中会频繁缺页。
如,工作窗口为:{2,3,5,3,2},进程的工作集是:{2,3,5}。工作窗口为:{4,2,1,1,5},进程的工作集是:{1,2,4,5}。
区的更快。
从何处调入页面就有三种情况
地址翻译(考试重点)