操作系统——内存管理

Posted xxwang1018

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作系统——内存管理相关的知识,希望对你有一定的参考价值。

一、存储器管理

1.1存储器的层次结构

1、多层结构的存储器系统

1)存储器的多层结构

通用计算机存储层次有三级:CPU寄存器,主存,辅存

主存包括:高速缓存、主存储器、磁盘缓存

辅存包括:固定磁盘、可移动存储介质

寄存器、高速缓存、主存储器和磁盘缓存属于操作系统存储管理的管辖范畴,断电后存储信息丢失;固定磁盘、可移动存储介质属于设备管理的管辖范畴,存储信息被长期保存

2)可执行存储器

寄存器和主存储器被称为可执行存储器

进程可以在很少的时钟周期内使用 load和 store指令访问可执行存储器,但对辅存的访问需要通过 I/O设备实现,耗费时间远超前者

2、主存储器与寄存器

1)主存储器

简称内存或主存,用于保存进程运行时的程序和数据

通常处理器从主存中取得指令和数据,将指令放入指令寄存器,将数据装入数据寄存器;或者反之

CPU与外围设备交换的信息一般依托于主存储器的地址空间

主存储器访问速度远低于 CPU指令执行速度,因此引入寄存器和高速缓存

2)寄存器

寄存器是有限存储容量的高速存储部件,用来暂存指令、数据和地址

在中央处理器的控制部件中,有指令寄存器和程序计数器。在中央处理器的算术及逻辑部件中,有累加器

3、高速缓存和磁盘缓存

1)高速缓存

Cache介于寄存器和存储器之间,主要用于备份主存中常用的数据,以减少存储器对主存储器的访问次数

高速缓存容量远大于寄存器,比内存小很多,访问速度快于主存

Cache的产生是局部性原理的作用

2)磁盘缓存

磁盘的 I/O速度远低于都主存的访问速度,为了缓和两者在速度上的不匹配设置磁盘缓存,作用是暂时存放频繁使用的磁盘数据和信息以减少访问磁盘的次数

磁盘缓存不是实际存在的存储器,而是利用主存中的部分存储空间暂时存放从磁盘中读取或写入的信息

1.2程序的装入和链接

用户程序在系统中运行,必须先将他装入内存,然后再将其转变为一个可执行的程序,要经历以下步骤:

  1. 编译:由编译程序对用户源程序进行编译,形成若干个目标模块
  2. 链接:由链接程序将编译后形成的一组目标模块以及它们所需要的库函数链接在一起,形成一个完整的装入模块
  3. 装入:由装入程序将装入模块装入内存

1.2.1程序装入

1、绝对装入方式

当计算机系统很小且仅能运行单道程序,完全有可能知道程序将驻留在内存的什么位置时采用此方式

装入模块装入内存后,由于程序中的逻辑地址与实际内存地址完全相同,不需对程序和数据的地址进行更改

2、可重定位装入方式

在多道程序环境下,对于用户程序编译形成的若干目标模块,它们的起始地址通常从 0开始,程序中的其它地址基于起始地址计算。此时采用可重定位装入方式

该方式会使装入模块中的所有逻辑地址与实际装入内存后的物理地址不同,要对数据和指令的地址进行修改,加上该装入模块在内存中的始址

地址变换在进程装入时一次完成,之后不再改变,故称为静态重定位

3、动态运行时的装入方式

该方式可将装入模块装入内存中任何允许的位置,可用于多道程序环境,但不允许程序运行时在内存中移动位置

装入模块装入内存后,其中的逻辑地址不会立即转换成物理地址,而是推迟到程序真正要张旭时才进行。因此装入内存后仍是逻辑地址

1.2.2程序链接

根据链接时间的不同划分

1、静态链接方式

在程序运行之前,先将各目标模块及它们所需的库函数链接成一个完整的装入模块,以后不再拆开

链接成完整的装入模块时需解决两个问题:

  1. 对逻辑地址进行修改。原模块在装入模块中的地址不再是 0,而要加上前面所有模块的长度和
  2. 变换外部调用符号。当一条具体语句调用外部模块时,外部模块地址已经发生改变,语句中的地址也要改变

2、装入时动态链接

装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序找出相应的外部目标模块,并将它装入内存,然后按照静态链接方式的方法修改目标模块中的相对地址

3、运行时动态链接

在执行过程中,当发现一个被调用模块尚未装入内存时,立即由 OS找到该模块并将之装入内存,将其链接到调用者模块上

1.3交换

1、多道程序环境下的交换技术

1)交换的引入

在多道程序环境下,某些进程被阻塞运行但占据了大量的内存空间,而许多作业因内存空间不足一直驻留在外存,造成了严重的浪费

交换是指把内存中暂时不能运行的进程或者暂时不用的程序和数据换出到外存上,腾出足够的空间,再把已具备运行条件的进程或进程所需要的程序和数据换入内存

2)交换的类型

整体交换:处理器中级调度就是存储器的交换功能

页面(分段)交换:以进程的一个页面或分段为单位进行

2、进程的换出与换入

1)进程的换出

  • 选择被换出的进程。首先选择处于阻塞或睡眠状态的基础,存在多个时选择优先级最低的
  • 进程换出过程。只能换出非共享的程序和数据段;若内存中还有可换出的程序,继续执行直到内存中无阻塞进程

2)进程的换入

定时执行换入操作

  • 首先查看 PCB集合中所有进程的状态,找出”“就绪状态但已换出的进程。若有多个,选择换出时间最久的
  • 成功换入一个进程后,继续执行直到内存中没有”就绪且换出“状态的进程或内存不足为止

1.4连续分配管理方式

该方式为一个用户程序分配一个连续的内存空间,即程序中代码或数据的逻辑地址相邻体现在内存空间分配时物理地址的相邻

1.4.1单一连续分配

在单道程序环境下,内存被分成系统区和用户区两部分,系统区仅提供给 OS使用,放在内存的低址部分,用户区则被一道用户程序独占。这种存储器分配方式称为单一连续分配方式

1.4.2固定分区分配

为了能在内存中装入多道程序,且使这些程序之间不会相互干扰,于是将整个用户空间划分为若干固定大小的区域,每个分区只装入一道作业

1、划分分区的方法

大小相等:对利用一台计算机同时控制多个相同对象的场合比较实用

大小不等:可根据程序大小适当分配

2、内存分配

将分区按大小进行排队,建立一张分区使用表,各表项包括每个分区的起始地址、大小及状态(是否已分配)

当某一用户程序要装入时,内存分配程序根据用户程序大小检索该表,找出一个满足要求的、尚未分配的分区,将之分配给该程序,然后将该表项中的状态置为“已分配”。若未找到合适的分区则拒绝为该用户程序分配内存

1.4.3动态分区分配(含顺序和索引算法)

基础概念

涉及三个方面:分区分配所用的数据结构,分区分配算法,分区的分配与回收操作

1、数据结构

常用的有两种

  • 空闲分区表:记录每个空闲分区的情况,每个空闲分区占一个表目,表目中包括分区号、分区大小和分区始址等数据项
  • 空闲分区链:每个分区的起始部分设置一些用于控制分区分配的信息,以及链接各分区所用的前向指针,分区尾部设置一后向指针。通过两个指针链接成一个双向链

2、算法

顺序式搜索算法,索引式搜索算法(见后面)

3、分区分配操作

①分配内存

设请求分区大小为 U,每个空闲分区大小为 M,事先规定不可再分的剩余分区大小为 size

若 M - U <= size,说明剩余部分太小不能再分割给其它程序,则将整个分区分配给请求者

否则,按请求的大小划分内存空间分派出去,将分配区的始址返回给调用者

②回收内存

若回收区与空闲分区相邻,把它们合并成新的空闲分区,大小为它们的和,首地址为它们中始址最小的那个

否则,为回收区单独建立一个新表项,填写回收区的始址和大小,并根据始址插入到空闲链的适当位置

基于顺序搜索算法

1、首次适应算法 FF

分配内存时从链首开始顺序查找,直至找到满足要求的空闲分区

2、循环首次适应算法 NF

每次从上次找到的空闲分区的下一个空闲分区开始查找,而不是每次从链首开始

实现该算法需要设置一个起始查询指针,如果最后一个空闲分区仍不能满足要求,则返回到第一个空闲分区进行比较,并依次后延。找到后调整起始查询指针

3、最佳适应算法 BF

将所有的空闲分区按容量从小到大形成空闲分区链,顺序查找就能找到满足要求又最小的空闲分区。但是会留下很多碎片

4、最坏适应算法 WF

策略与最佳适应算法正好相反,空闲分区按容量从大到小排序,每次看第一个分区能否满足作业要求

基于索引搜索算法

为提高搜索空闲分区的速度

1、快速适应算法

又称分类搜索法

将所空闲分区按容量大小分类,为具有相同容量的所有空闲分区单独设立一个空闲分区链表,这样系统中存在多个空闲分区链表。在内存中设立一张管理引擎表,每个表项对应一种空闲分区类型,并记录该类型空闲分区链表表头的指针

搜索可分配的空闲分区分为两步:

  1. 根据进程的长度从索引表中寻找能容纳它的最小空闲分区链表
  2. 从链表中取下第一块进行分配

2、伙伴系统

伙伴系统方式是对固定分区和动态分区方式的一种折衷方案

规定:无论已分配分区或空闲分区,其大小均为2的k次幂,k为整数,1 <= k<= m。其中 2的 1次方表示分配的最小分区的大小,2的 m次方表示分配的最大分区的大小,通常 2的 m次方是整个可分配内存的大小

①空间分配

  • 当需要为进程分配一个长度为 n的存储空间时,首先计算一个 i 值,使 n大于 2^(i-1) 次方,小于等于 2^ i 次方,然后在空闲分区大小为 2^ i 的空闲分区链表中查找。若找到,即把该空闲分区分配给进程。否则表明长度为2^i的空闲分区已经耗尽,则在分区大小为 2^(i+1) 的空闲分区链表中寻找
  • 若存在一个 2^(i+1) 的空闲分区,则把该空闲分区分为相等的两个分区,这两个分区称为一对伙伴,其中的一个分区用于分配,把另一个加入分区大小为 2^ i 的空闲分区链表中。若大小为 2^(i+1) 的空闲分区也不存在,则需要查找大小为 2^(i+2) 的空闲分区
  • 若找到一个 2^(i+2) 的空闲分区,则对其进行两次分割:第一次,将其分割为大小为 2^(i+1) 的两个分区,一个用于分配,一个加入到大小为 2^(i+1) 的空闲分区链表中;第二次,将第一次用于分配的空闲区分割为 2^ i 的两个分区,一个用于分配,一个加入到大小为 2^ i 的空闲分区链表中
  • 若仍然找不到,则继续查找大小为2^(i+3)的空闲分区,以此类推

由此可见,在最坏的情况下,可能需要对2^k的空闲分区进行k次分割才能得到所需分区

②空间回收

  • 与一次分配可能要进行多次分割一样,一次回收也可能要进行多次合并
  • 若回收大小为 2^ i 的空闲分区,若事先已存在 2^ i 的空闲分区,则应将其与伙伴分区合并为大小为 2^(i+1) 的空闲分区,若事先已存在 2^(i+1) 的空闲分区时,又应继续与其伙伴分区合并为大小为 2^(i+2) 的空闲分区,以此类推

在伙伴系统中,分配和回收的时间性能取决于查找空闲分区的位置和分割,合并空闲分区所花费的时间。与前几种动态分区分配算法相比较,由于该算法在回收空闲分区时,需要对空闲分区进行合并,所以其时间性能比前面所述的分类搜索法差,但比顺序搜索法好,而其空间性能则远优于分类搜索法,比顺序搜索法略差

3、哈希算法

利用哈希快速查找的优点以及空闲分区在可利用空闲分区表中的分布规律建立哈希函数,构造以空闲分区大小为关键字的哈希表,该表的每一个表项记录一个对应的空闲分区链表表头指针

进行空闲分区分配时,根据所需空闲分区大小,通过哈希函数计算,即得到在哈希表中的位置,从中得到相应的空闲分区链表,实现最佳分配策略

1.4.4动态可重定位分区分配

1、紧凑

通过移动内存中作业的位置把原来多个碎片拼接成一个大分区的方法称为紧凑

每次紧凑后都必须对移动的程序或数据进行重定位

2、动态重定位

程序在执行时,真正访问的内存地址是逻辑地址与重定位寄存器中的地址相加形成的

地址变换过程在程序执行期间随着对每条指令或数据的访问自动进行的

3、算法

与动态分区分配算法基本相同,差别在于增加了紧凑的功能

计算碎片总和与所需空间进行对比,满足要求则进行紧凑操作,反之返回失败信息

1.5非连续分配管理方式

1.5.1分页存储管理方式

1、基本方法

1)页面和物理块

将用户程序的地址空间分为若干个固定大小的区域,称为页或页面

将内存空间分为若干个物理块或页框,页和块大小相同

可将用户程序的任一页放入任一物理块以实现离散分配

2)地址结构

分页地址由两部分组成:页号和页内地址以32位长分页地址为例,0~11位是页内地址,即每页大小为 4KB,12~31位是页号,地址空间最多允许有 1M页

若给定一个逻辑地址空间中的地址为 A,页面大小为 L,则页号 P和页内地址 d可按下式求得P=INT[A/L],d=[A] MOD L。INT是整除,MOD是取余例如系统页面大小为 1KB,设 A=2170B,则由上式求得 P=2,d=122

3)页表

系统为每个进程建立一张页表

进程地址空间内的所有页在页表中有一个页表项,记录相应页在内存中对应的物理块号。通过查找该表即可找到每页在内存中的物理块号,即页表的作用是实现从页号到物理块号的地址映射

2、地址变换机构

通过页表实现用户地址空间中逻辑地址到内存空间中物理地址的转换

1)基本地址变换机构

页表数很大,但寄存器因为成本原因数目有限,因此页表大多贮存在内存中。系统中只设置一个页表寄存器,其中存放页表在内存的始址和页表的长度。当调度程序调度到某进程时才将存放在 PCB中的页表始址和长度装入页表寄存器

当进程要访问某个逻辑地址中的数据时,分页地址变换机构会自动将有效地址(相对地址)分为页号和页内地址两部分,再以页号为索引检索页表

检索之前,先将页号与页表长度进行比较,如果页号大于等于页表长度,表示本次访问的地址已超越进程的地址空间,产生地址越界中断

若没有出现越界错误,将页表始址与页号和页表长度的乘积相加,得到该表项在页表中的位置,进而得到该页的物理块号,将之装入物理地址寄存器。同时将有效地址寄存器中的页内地址送入物理地址寄存器的块内地址字段中。最终完成逻辑地址到物理地址的转换

2)具有快表的地址变换机构

由于页表存放在内存中,CPU存取数据需要访问两次,效率很低

为提高地址变换速度,在地址变换机构中增设一个具有并行查询能力的特殊 Cache,称为快表,用于存放当前访问的页表项

CPU给出有效地址后,由地址变换机构自动将页号送入快表,将此页号与 Cache中的所有页号进行比较

若其中有相匹配的页号则表示要访问的页表项在快表中,于是可直接从快表中读取该页对应的物理块号送入物理地址寄存器中

若没找到,访问内存中的页表,找到后读取该页对应的物理块号送入物理地址寄存器中。同时将此页表项存入快表的一个寄存器单元,即重新修改快表;若快表已满,OS必须找到一个被认为是不再需要的页表项换出

3、两级和多级页表

1)多级页表思想

如果页表很大,页表占用的内存物理块允许是离散的,且页表可以按需装入内存,就可以建立页表的页表即页目录,这就是二级页表机制。如果需要还可建立更多级的页表

无论多少级页表,页目录必须完全驻留内存

2)二级页表

系统为每个进程建一张页目录表,其每个表项对应一个页表页,页表页的每个表项包括页和块的对应关系

页目录表是一级页表,页表页是二级页表

二级页表的地址结构由页目录号、页号、页内位移组成

二级页表的地址转换:

CPU给出的逻辑地址分解为三部分:页目录号、页号、页内位移

根据页目录号查找页目录表中相应表项,获得页表页所在物理块号

在物理块中查找页表页号,获得该页对应物理块号,该物理块号与页内位移一起构成物理地址

4、反置页表

与页表恰好相反,反置页表是物理地址到虚拟地址的映射

反置页表记录内存中每个物理块存放哪个进程的哪一页,其内容是页号和隶属进程的标识符

系统为每个物理块设置一个页表项并按物理块号排序

反置页表的地址结构由进程标识符、页号、页内位移组成

反置页表的地址转换:

CPU给出的逻辑地址分解为三部分:进程标识符、页号、页内位移

利用进程标识符和页号检索反置页表以获得该页所在物理块号

如果检查到匹配表项,该表项的序号 i 便是该页所在的物理块号,将该物理块号与页内地址一起构成物理地址;若检索完都没有找到匹配项,说明未调入内存,请求调页或返回地址出错信息

1.5.2分段存储管理方式

1、分段

作业的地址空间被分为若干个段,每个段定义一组逻辑信息。段的长度 SL由逻辑信息组的长度决定

分段地址由段号 S(段名)和段内地址 d组成

2、段表

需要为每个进程建立段映射表,简称段表,每个表项记录某段在内存的起始地址和段的长度

3、地址变换机构

系统中设置段表寄存器存放段表始址和段表长度 TL

首先将逻辑地址中的段号 S和段表长度 TL比较,若 S > TL,表示段号太大导致越界访问,产生越界中断;反之根据段表始址和该段段号计算段表项位置,读取该段在内存的始址

然后比较段内地址 d和段长 SL,若 d > SL,产生越界中断,反之将段内地址与该段始址相加得到要访问的内存物理地址

4、分页和分段的区别

  • 页是信息的物理单位,出于系统管理的需要,是系统的行为。段是信息的逻辑行为,为了满足用户需求
  • 页的大小固定且由系统决定。段的长度根据实际划分不定
  • 分页的用户程序地址空间是一维的。分段的是二维的,标识地址要给出段名和段内地址

1.5.3段页式存储管理方式

1、基本原理

先将用户程序分成若干段,再把每个段分成若干页

地址结构由段号、段内页号、页内地址组成

2、地址变换过程

需要同时配置段表和页表,段表内容换成页表始址和页表长度。配置段表寄存器存放段表始址和段表长度

首先比较段号 S和段表长度 TL,若 S < TL,未越界,利用段表始址和段号计算该段对应段表项位置,获得该段的页表始址,利用逻辑地址中的段内页号 P获得对应页的页表项位置,获得该页所在物理块号,利用物理块号和页内地址构成物理地址

分页和分段系统访问一个数据需要访问内存两次,段页系统需要三次

二、虚拟存储器

2.1基本概念

1、定义

具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的存储器系统

2、特征

多次性:允许将作业分多次调入内存运行,即只需将当前运行需要的部分程序和数据装入内存即可运行

对换性:进程运行期间,允许将暂不运行的程序和数据换出,以后需要再换入。甚至允许将暂不运行的基础调出

虚拟性:逻辑上扩充容量,使用户看到的容量远大于实际的

3、实现方法

建立在离散分配存储管理方式的基础上

1)分页请求系统

分页系统的基础上增加请求调页功能和页面置换功能

允许用户程序装入少量页面即可启动运行,以后通过上述增加功能调入即将运行页,调出暂不运行页

置换时以页为单位

2)请求分段系统

类比分页请求系统

2.2请求分页存储管理方式

2.2.1请求分页中的硬件支持

1、请求页表机制

每个页表包括 6个字段:页号,物理块号,状态位 P,访问字段 A,修改位 M,外存地址

  • 状态位:指示该页是否已调入内存
  • 访问字段:记录本页一段时间内被访问的次数,或最近多久未被访问
  • 修改位:标识该页调入内存后是否被修改过。没有则不用写回外存,有则重写到外存以保证外存保留副本始终最新
  • 外存地址:指出该页在外存的地址,通常是物理块号

2、缺页中断机构

请求分页系统中,每当要访问的页面不存在时会产生缺页中断,然后请求 OS将缺页调入内存

与一般中断区别:

  1. 在指令执行期间产生和处理中断信号
  2. 一条指令在执行期间可能产生多次缺页中断

3、地址变换机构

首先检索快表找出要访问的页,若找到便修改页表项中的访问字段,再将修改位改成 1,最后利用页表项的物理块号和页内地址形成物理地址。若未找到该页页表项,到内存中查找页表,从页表项的状态位了解该页是否已调入内存;若调入内存将页表项写入快表,快表已满时进行页的转换;若未调入内存则产生缺页中断,请求 OS调入

2.2.2请求分页中的内存分配

1、最小物理块数的确定

最小物理块数是能保证进程正常运行所需的最少物理块数

2、内存分配策略

固定分配:为每个进程分配一组固定数目的物理块,进程运行期间不做改变

可变分配:先为每个进程分配一定数目的物理块,进程运行期间做适当的增减

全局置换:若进程运行中发现缺页,将 OS保留的空闲物理块取一个给它。或者在所有进程的全部物理块中选择一个进行交换,这样分配给该进程的内存空间就会增加

局部置换:若进程运行中发现缺页,只能从分配给该进程的 n个页中选一个进行交换,以保证分配给该进程的内存空间保持不变

三种策略:固定分配局部置换,可变分配全局置换,可变分配局部置换

3、物理块分配算法

平均分配算法,按比例分配算法,考虑优先权的分配算法

2.2.3页面调入策略

1、何时调入页面

预调页策略:预测将来不久会被访问的页面进行调入

请求调页策略:访问时发现不存在,请求 OS调入

2、从何处调入页面

请求分页系统的外存分成存放文件的文件区存放交换页面的交换区。前者采用离散分配方式,后者采用连续分配方式

发生其它请求时,系统分三种情将缺页调入内存

  1. 系统拥有足够的交换区空间。全部从交换区调入所需页面;为此需要在进程运行前从文件区拷贝所有文件到交换区
  2. 系统缺少足够的交换区空间。不会被修改的文件直接从文件区调入;可能被修改的文件换出时必须调入交换区,以后需要时再换入
  3. UNIX方式。未运行过的页面从文件区调入,曾运行过但被换出的页面放在交换区

3、页面调入过程

技术图片

4、缺页率

设进程的逻辑空间为 n页,分配的内存物理块数为 m。访问页面成功次数为 S,失败次数为 F

则缺页率:f = F / (S + F)

设被置换的页面被修改概率 B,其缺页缺页中断处理时间为 Ta;被置换页面但没有被修改的缺页的中断时间为 Tb

则缺页中断处理时间:T = B * Ta + (1 - B) * Tb

2.3页面置换算法

1、最佳置换算法

理想化算法,无法实现

当缺页发生时,操作系统无法知道各个页面下一次是在什么时候被访问。虽然该算法不可能实现,但是它能用于对可实现算法的性能进行衡量比较,是一种很好的评价算法

选择淘汰的页面是以后永不使用的,或在未来最长时间内不再被访问的页面

2、先进先出算法 FIFO

把一个进程已调入内存的页面按先后次序链接成一个队列,设置一个指针,即替换指针。使它总指向最老的页面

3、最近最久未使用算法 LRU

为每个页面赋予一个访问字段,记录一个页面自上次被访问以来经历的时间。选择时间值最大的作淘汰页

4、最少使用置换算法 LFU

为在内存中的每个页面设置一个移位寄存器,记录该页面被访问的次数。选择最近使用最少的作淘汰页

2.4“抖动”与工作集

1、“抖动”

刚被置换出的页很快又被访问,需要重新调入,然后再选一页调出;而刚被调出的页又很快被访问,需要再次调入。如此频繁的更换页面,以致一个进程运行的大部分时间都花费在页面置换上,称该进程发生了“抖动”

“抖动”的发生与系统为进程分配物理块的多少有关

2、工作集

在将来某段时间间隔里进程要访问的页面的集合称工作集

为防止产生缺页,应将程序的全部工作集装入内存中

把程序过去某段时间内的行为作为程序在将来某段时间内行为的近似来进行预测,是构造工作集的依据

以上是关于操作系统——内存管理的主要内容,如果未能解决你的问题,请参考以下文章

《java精品毕设》基于javaweb宠物领养平台管理系统(源码+毕设论文+sql):主要实现:个人中心,信息修改,填写领养信息,交流论坛,新闻,寄养信息,公告,宠物领养信息,我的寄养信息等(代码片段

使用 Git 来管理 Xcode 中的代码片段

java内存流:java.io.ByteArrayInputStreamjava.io.ByteArrayOutputStreamjava.io.CharArrayReaderjava.io(代码片段

massCode 一款优秀的开源代码片段管理器

如何管理在每个 git 版本中添加私有代码片段?

如何使用Android片段管理器传递变量[重复]