内存扩充技术
为了解决内存不够用,在逻辑上扩充内存,提出了内存的覆盖与交换技术。
覆盖技术
基本思想
- 对于一个进程,不需要一开始就把程序的全部指令和数据都装入内存再执行。
- 将进程划分为若干个功能上相对独立的程序段,按照程序逻辑结构让那些不需要同时执行的程序段共享同一块内存区。
- 当有关程序段的先头程序段执行结束后,再把后续程序段从外存调入内存覆盖前面的程序段
要求
程序员必须把一个程序划分成不同的程序段,并规定好它们的执行和覆盖顺序。
覆盖技术可以节约内存空间
覆盖技术可以通过节约内存空间的使用来变相的扩充内存空间。
核心思想:不相互调用的程序段可以共享同一内存区。
例子:设某进程的程序正文段由A,B,C,D,E和F等6个程序段组成。进程的程序段结构如下图所示:
由于程序段B不调用C,程序段C也不调用B,因此程序段B和C无需同时驻留在内存,它们可以共享同一内存区。同理,程序段D、E、F也可共享同一内存区。
对于内存来说,它可以分为2个部分:
- 常驻内存部分:与所有被调用程序段有关,不能被覆盖。这一部分里面保存的程序被称为根程序。如上图 (b)中,根程序是程序段A。
- 覆盖区:可以被反复覆盖的区域。如上图(b)中的覆盖区0和1
那么,覆盖技术是如何节约内存的呢?请看下面介绍:
不采用覆盖技术:
整个进程正文段要求内存空间:(如下图(a)所示)
A(20K)+B(50K)+F(30K)+C(30K)+D(20K)+E(40K)=190K
采用覆盖技术:
覆盖区0由程序段B、C共享,所需内存容量为50K。
覆盖区1为程序段F、D、E共享,所需内存容量为40K。
采用覆盖技术,只需110K的内存空间即可开始执行。如下图(b)所示。
交换技术
如果把处于等待状态和运行状态的进程都放到内存中,无疑会降低内存利用率,而且对于需要消耗大内存的进程来说这种做法也不现实。因此,我们可以使用交换技术来提高内存利用率,这相当于变相的扩充内存。
基本思想
操作系统把阻塞状态进程的整个地址空间的内容换出内存,而将外存中的某个就绪进程的整个地址空间换入内存中。
相对与交换技术的优点
与覆盖技术相比,交换技术不要求程序员给出程序段之间的覆盖结构。
交换技术和覆盖技术的区别
- 交换主要是在不同进程(作业)之间进行。
- 覆盖主要在同一个进程(作业)内进行,只能对没有关联的程序段进行覆盖。
交换和覆盖与虚拟内存技术的区别
虚拟内存技术和交换技术很像,乍一看都是换入换出,把暂时不需要用的数据换出内存,将需要用到的数据换入内存,从而实现逻辑上内存的扩充。
虚拟内存技术和交换技术的区别是:
- 交换技术是以进程为单位,若进程所需内存大于系统内存 ,则此进程无法进行。交换技术是在不同的进程(作业)间的。
- 虚拟存储是以页或段为单位,是把进程再分为页或段对内存进行分化,若进程所需内存大于系统内存,进程也可以运行,因为该进程的一部分可换到外存上。虚拟存储技术是在一个作业间的。
用下图总结这3中技术的区别:(了解下就行)
物理内存管理
进程在运行时需要使用内存,我们该如何管理这些内存,使进程能够正常且高效的运行下去呢?
连续内存分配管理
连续内存分配是指给进程分配一块连续的物理内存区域,用来装入整个进程(即能装下整个进程的逻辑地址空间)。连续内存分配主要有3种方式:
①单一连续分配 ②固定分区分配 ③动态分区分配。
1.单一连续分配
在单一连续分配方式中, 我们不需要进行地址转换,程序生成时使用的是物理地址(即它的逻辑地址就等于物理地址)。内存被分为系统区和用户区。系统区通常位于内存的低地址部分,用于存放操作系统相关数据;用户区位于高地址部分,存放用户进程相关数据。内存中只能有一道用户程序,用户程序独占整个用户区空间,自然也不需要操作系统提供存储保护。
优点:实现简单;无外部碎片;可以采用覆盖技术扩充内存;不一定需要采取内存保护(eg:早期的 PC 操作系统 MS-DOS)。
缺点: 只能用于单用户、单任务的操作系统中;有内部碎片;存储器利用率极低
2.固定分区分配
为了克服单一连续分配只能用于单用户单任务的情形,能在物理内存中装入多道程序,且这些程序之间又不会相互干扰,于是将整个物理内存的用户区划分为若干个固定大小的分区,每个分区只装入一道程序,只要分区的大小大于等于进程的大小即可。
这样就形成了最早的、最简单的一种可运行多道程序的内存管理方式。
内存分配过程
当某用户程序要装入物理内存时,由操作系统内核程序根据用户程序大小检索分区表,从中找到一个能满足大小的、未分配的分区,将它分配给该程序,然后修改状态为“已分配”:
固定分区特点
注意,在固定分区分配方案中,逻辑地址和物理地址不再相等,它们之间的转换过程如下图所示。对于当前正在运行的进程,CPU检查它的逻辑地址范围是否在当前分区的大小限制之内,如果超出分区大小则报错。否则,取出当前分区的起始地址,与逻辑地址相加就得到了物理地址:
2种内存划分方法
固定分区分配在划分每个分区的大小时,有2种不同的划分方法:分区大小相等 和 分区大小不等:
分区大小相等:缺乏灵活性,程序太大时候,一个分区又不足以装入该程序,导致程序无法运行。
分区大小不等:具有较高的灵活性,但是查找合适的空闲分区比较复杂。
固定分区分配的优缺点:
优点:
- 支持多道程序。
- 实现简单,无外部碎片。
缺点:
- 当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这又会降低性能;
- 程序小于分区的大小时会产生内部碎片,内存利用率低。
3.动态分区分配
为了克服固定分区的缺点,出现了动态分区的方法。
动态分区分配不会预先划分物理内存分区,而是在进程装入物理内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此分区的大小和数目是可变的。
内存分配过程
如下图所示,系统有64MB物理内存空间,其中低8MB固定分配给操作系统,其余为用户可用内存。开始空闲内存为56MB,当需要运行进程1时,进程1需要20MB内存,于是为其分配20MB内存,接着把进程1装入内存。同样的方式为进程2 和 进程3分别分配14MB、18MB内存,接着把进程2和进程3装入内存:
内存外部碎片
紧接着上个例子,在某个时刻,操作系统换出进程2,换入进程4。由于进程4比进程2小,于是产生了一个6MB的外部碎片。之后,操作系统又换出进程1,换入进程2,又产生了6MB的外部碎片:
动态分区特点
在动态分区分配方案中,其逻辑地址和物理地址不再相等,它们之间的转换过程如下图所示:
动态分区的分配策略
为了尽可能高的提高内存利用效率,动态内存分配策略有以下几种:
- 首次适应算法
- 最佳适应算法
- 最坏适应算法
- 邻近适应算法
动态分区分配的优缺点:
优点:
1)支持多道程序(共用是指多进程同时存在于内存中的不同位置);【对比固定分区
缺点:
1)存在外部碎片,内存利用效率低
非连续内存分配管理
在连续内存分配中,要给一个进程分配内存,必须分配一块物理地址连续的内存区域,能装入整个进程,但是这种方式并不太合理,内存利用效率低。基于此,非连续内存分配就出现了。
非连续内存分配方式就是把一个进程分成几块,分别放入几个不同的分区。这样的话,一个进程就可以使用非连续的物理地址空间,但跟连续内存分配一样,还是要把整个进程装入到内存中。
非连续内存分配方式根据分区大小是否固定,分为:分页存储管理 和 分段存储管理。
分页存储管理分为基本分页存储管理 和 请求分页存储管理,这里暂时只讲基本分页存储管理。
在介绍这2中分配方式前,我们首先要解决的是逻辑地址到物理地址的转换。
在连续内存分配中,操作系统给程序分配一段连续的物理空间去使用,这时逻辑地址和物理地址都是连续的一一对应的关系,比如说逻辑地址d在a后面的第三个位置,那么在实际物理空间中d的映射也在a的映射的后面的第三个位置,这就是连续的内存分配。
而在非连续内存分配中,程序分配到的物理内存是不连续的。比如说逻辑地址空间d在a后面三的位置,但在实际物理空间中,d 对应的物理空间地址可能和 a 对应的物理空间地址差了十万八千里,即实现了内存的不连续分配。因此,用到了页表。
基本分页存储管理
分页存储管理的设计
分页存储管理的设计思想如下:
- 将进程的逻辑地址空间划分成多个大小相同的页(page),或称页面
- 将物理地址空间划分为多个大小相同的页框(page frame),或称为页帧,里面存放页。
- 每个页的大小和页框的大小是一样的。
- 为了知道进程的每个页面在物理内存中存放的位置,操作系统要为每个进程建立一张页表。
- 进程的页表在物理内存中是以内存连续的方式存放。
- 进程请求的页数不应该超过系统剩余的页框数。
- 页面的大小通常设为4KB。当然,如果没有装满4kB就出现了内存碎片。这个碎片和连续内存分配产生的碎片相比,还是可以接受的。
分页存储管理的地址转换
(1)分页存储管理的逻辑地址结构如下图所示,地址长度为32位:
可见,逻辑地址的12~31位为页号P,最多允许有2^20页;0~11位为页内偏移量W,也就是页内地址,每页大小为4KB。
(2)页表
页表用来记录页面在物理内存中对应的页框(物理块),其存储在内存中。页表的每一行称为页表项,由页号跟块号(页框号)组成,而且页号跟逻辑地址结构里的页号是一个东西。
CPU想找到进程对应的页面,首先要访问页表,找到页面对应的块号(页框号),然后拿着页框号,在物理内存中找到对应页框,就可以获取到页面了。
(3)逻辑地址转物理地址
在了解完逻辑地址结构体和页表后,接下来介绍如何使用他们来获取物理地址。
简单说明下,在系统中通常会有一个页表寄存器(PTR),存放页表在物理内存的起始地址 F 和 页表长度 M (页表项的个数)。假设页面大小为 L,页框号为 b,逻辑地址为 A,物理地址为 E,那么,物理地址 E = b * L + W。
块表TLB 与 慢表
通过上面的地址转换过程可知,若存取一条数据或指令至少要访问两次内存:一次是访问页表,计算出对应的物理地址,另一次是根据物理地址存取数据或指令。当需要大量存取数据时,这种方式显然效率不高。
为此,在地址转换机构中增加一个高速缓存器--快表(或称TLB),操作系统会把最近经常访问的页表项放入快表中。
既然有了快表,那对于内存中的页表来说,它自然就成了慢表了。
当引入快表时,若存取一条数据或指令时可能只需访问一次内存:首先会到快表中查找对应的页号,若找到,计算出对应的物理地址,直接存取数据或指令;若查不到,则返回到页表上找。由于快表是高速缓存器,访问快表比访问内存快多了,所以可以节省时间,而且快表肯定比页表小,所以查询起来,查询成本也低。
带有块表的地址转换机构如下图所示:
多级页表
一级页面的缺点:
问题1. 由于页表需要用连续的物理内存来存放,因此当页表很大的时候,需要占用很多个连续的页框。
问题2.由于时间局部性原理,一个进程在一段时间内可能只需要访问某几个特定的页面,因此没必要让整个页表都常驻内存。
接下来,分别介绍如何解决这两个问题。
问题1:解决页表必须要连续存放的问题。
使用多级页表,意思就是把连续的页表像进程一样进行分解成几组。每组扔进内存中空闲的页框中,这样连续存放变成离散存放了。但是对应的,为了检索离散分配的页表,我们也必须再构造一个连续的页表(叫页目录表)用于检索离散存储的页表。
例如,下面是两级页表结构图,10位用来表示一级页号,总共可表示1024个页号,同理,二级页号最多也有1024个。
这样就可以把连续的页表分成离散的多个部分存放了。但是要额外占用一个页目录表的空间。
问题2:一个进程在一段时间内可能只需要访问某几个特定的页面,因此没必要让整个页表都常驻内存。
可以在需要访问页面时,才把页面从外存调入内存,所以可以在页表项中增加一个标志位,用于表示该页面是否调入内存。若想访问的页面不在内存中,则会产生缺页中断,然后将目标页面调入内存。
从外存调入页面和从内存调出页面的设计,已经不是基本分页存储管理了,而是请求分页存储管理。(请求分页存储管理是虚拟内存的范畴,下面会介绍)
多级页表的优缺点:
优点:可以以更小的空间存储页表,例如有两级页表,一级页表肯定比二级页表小嘛,因为一级页表是二级页表的索引,当然还要加上一个条件,就是二级页表也是会被调出外存的,这个时候,才能说多级页表能减少存储空间。
缺点:额外增加访存次数。
基本分段存储管理
与分页的不同就在,分页存储方式的页面大小是一样的。而存储方式的页面大小可以是不一样的。类似于固定分区分配和动态分区分配的区别。
分段存储管理的设计
分段:按照程序自身逻辑关系将其逻辑地址空间划分为多个段,每个段在物理内存中占连续空间,但是各个段之间可以不相邻。
从上图可以看出,程序被分成多个段,分别存放在内存中的不同位置。
分段存储管理的地址转换
(1)分段存储管理的逻辑地址结构跟分页存储的基本一样,如下图所示,地址长度为32位:
可见,逻辑地址的16~31位为段号S,最多允许有2^16=65536个段;0~15位为段内偏移量W,也就是段内地址,最大段长为64KB。
(2)段表
跟页表一样,操作系统会为每个进程建立一张段表,用于记录每个段在物理内存中的起始位置与大小。段表和段表中的每一项的结构体如下图所示:
(3)逻辑地址转物理地址
分段存储管理的逻辑地址转物理地址方式跟分页基本一样,这里不再赘述。
分段和分页的对比
分段比分页更容易实现数据的共享和保护。
怎么理解呢?为了实现分段共享,可在系统中配置一张共享段表,所有各共享段都在共享段表中占有一表项。非共享段仅为一个进程所需要。当进程不再需要该段时,可立即释放该段;共享段是为多个进程所需要的,仅当所有共享该段的进程全部不再需要该段时,才可释放该共享段。对于实现分段保护来说,可以在段表的每一项中,设置一个“存取控制”字段,用于规定进程对该段的访问方式。
分段和分页的优缺点对比如下:
段页式存储管理
分页存储能有效提高内存利用率,而分段存储能按照程序逻辑实现数据的共享和保护。因此,将这两种方式结合起来,就构成了段页式管理。
段页式是将进程先分段,然后再对段进行分页。
段页式存储管理的设计
段页式:先把进程的逻辑地址空间按照逻辑关系分成多个段,再将每个段分成多个大小相同的页。对于物理内存的管理方式仍然和分页管理一样,将其分成多个和页面大小相同的页框。
段页式存储管理的地址转换
(1)段页式存储管理的逻辑地址结构如下图所示,地址长度为32位:
(2)段表和页表
跟页表一样,操作系统会为每个进程建立一张段表,每个段表有一张页表,用于记录逻辑地址到物理地址的映射。
注意:在一个进程中,段表只能有一个,而页表可能有多个。
在访问数据时,首先通过段表找到页表,在根据页表找到物理地址,然后根据物理地址找到数据,实际需要三次访问物理内存。
虚拟内存技术
为什么会诞生虚拟内存技术
前面介绍的连续内存分配管理和非连续内存分配管理,都存在如下2个问题:
- 进程必须全部放入物理内存后,才能开始运行。
- 进程被装入物理内存后,就一直驻留在内存中,不会有任何部分被换出,直至进程运行结束。
这2个问题,会造成内存利用率低,对多进程来说并不友好。那么,能不能不把整个进程全部装入内存也能让程序正常运行呢?当然有,局部性原理能够在理论上让这种做法具有可行性。
局部性原理:程序中的某条指令或数据一旦被执行或访问,不久后该指令或数据有很大的概率会被再次执行或访问。
根据局部性性原理,我们可以把用不到的进程页面从内存调到外存中,当需要使用时再调入内存。这样一来,同样大小的内存,可以同时运行更多的进程,让用户感觉到好像内存变大了很多。这就是虚拟内存技术。实际上,虚拟内存技术就是分页、分段、段页式的改进。
首先说明,现代操作系统使用的内存管理方式是: 分段+分页相结合的内存管理方式,而且还是多级页表的方式。
可为什么现在讲虚拟内存的时候,都是讲分页,很少讲分段呢?这一切的一切,都是因为一个原因:操作系统通过巧妙的设置,‘屏蔽’了段的存在。操作系统将进程的4G虚拟地址空间只分成了一个段。说起来是分段,实际上等于没分。所以, 虚拟内存的核心是分页管理。
请求分页存储管理
原理
在请求分页中,如果所需要的页面不在内存中,就会产生一个缺页中断,然后由操作系统的缺页中断程序来处理。此时缺页的进程阻塞,并进入阻塞队列,当调页码完成后,再将它唤醒,放入就绪队列。
此时,如果内存中有空闲的页框,则会把所缺页面装入这个页框。如果内存中已经没有空闲的页框,则由页面置换算法选择一个页面淘汰掉。
请求分页和传统分页的分别
1、当需要的页面不在内存中,由操作系统负责将所需要的页面从外存调入内存。
2、当内存空间不够时,由操作系统将内存中暂时用不到的页面换出到外存。
从上面的表述可以看到,内存和外存的调入调出都要经过操作系统的。所以就意味着肯定会产生中断。这也意味着其实会有额外的开销。
请求分页的页表机制
先回顾下,传统的基本分页机制的页表:
请求分页的页表:由于请求分页方式可能会把某些页面放到外存,因此此页表不仅要记录,内存位置,还要记录外存的位置等。
- 状态位:标记该页是否已调入内存
- 访问字段:记录该页最近被访问过几次,或记录上次访问的时间
- 修改位:该页调入内存后是否被修改过
- 外村地址:该页在外存中的地址,通常是物理块号
下图是请求分页存储管理的页表:
页面置换算法
由于页面的换出、换入是需要用到磁盘I/O的(毕竟与外存交互),所以好的页面置换算法需要追求更小的缺页率。
页面置换算法:确定要从内存中调哪一个页面到外存去。
常见的页面置换算法有四种:
-
先进先出置换算法(FIFO):替换最早进来的页面
-
最近最久未使用置换算法(LRU):替换的页面都是最近最久未被使用的页面。
-
时钟置换算法(CLOCK):将内存中的页面都通过链接指针连接成一个循环队列,当某页被访问时,将页面的访问字段为1。当需要淘汰页面时,只需检查页的访问字段,如果是0,则换出,如果是1则把它置为0,暂不换出,继续检查下个页面。若第一轮扫描中,页面的访问字段都是1,则把1都置0,然后启动第二轮扫描,第二轮扫描肯定会有0的页面。所以这种方法最多会经过两轮扫描。
-
改进型的时钟置换算法:普通的时钟置换算法,只考虑一个页面最近有没有被修改过,但是改进型时钟算法除了考虑最近是否被访问过之外,也考虑了页面有没有被修改过,因为没有修改过的页面是不需要花额外开销去执行I/O操作把他重新写回外存的,所以在其他条件相同时,优先换出没有修改过的页面。同样在页面中,维护一个(访问字段,修改位),例如(1,1)为最近被访问过且被修改过。
页面分配算法
之前的页面置换算法目的是为了从内存调出页面到外存时,选择哪个页面需要被淘汰。
这里的页面分配算法目的是为了从外存调入页面到内存时,要选择哪个页框来存放。
首先要明白一个概念:
驻留集:指请求分页存储管理中给进程分配的物理块(页框)个数。
1、若驻留集太小,则会造成进程频繁缺页,系统处理花费大量时间通过I/O从外存往内存里调页。
2、若驻留集太大,则会导致进程并发度下降,资源利用率低,因为进程频繁使用的页面小于驻留集,则该进程的驻留集中很多都是使用频率较小的页面,所以会导致并发度低,资源利用率低。
所以如何决定进程的驻留集大小呢?有下面3个方法:
1.固定分配局部替换:
系统为每个进程分配固定数量的内存块(页框),整个运行过程都不变,若发生缺页,只能从进程已有的物理块中选择一页换出,并调入所需页面。
缺点:很难在一开始就决定一个进程到底分配多少物理块才合理。
2.可变分配局部替换:
刚开始会被进程分配一定数量的物理块,当某进程发生缺页时,只允许从进程自己的物理块中挑出一个进行淘汰,但如果发生频繁缺页,系统会被这个进程多分配几个物理块,直到该进程的缺页率减少到一定程度,反之,如果进程在运行过程中,缺页率特别低,则适当减少该进程的物理块。(要根据发生缺页的频率来动态增加或减少进程的物理块)
3.可变分配全局替换:
刚开始会被进程分配一定数量的物理块,且会维持一个空闲物理块队列,当某进程发生缺页时,从空闲物理块队列中挑出一页分配给该进程,若已经没有空闲的物理块,则选择调出的的页面有空能是系统中任何一个进程的页面。(只要发生缺页就分配新物理块)
内存抖动
刚刚换出的页面马上又要换入内存,刚刚换入内存的页面马上又要换出。这种频繁页面调度的行为就叫内存抖动。
产生的原因:进程频繁访问的页面数大于进程可用的物理块数。
解决方法:工作集
工作集:在某段时间内,进程实际访问的页面集合。
工作集的大小其实不是固定的,但我们只要保证进程的驻留集(进程拥有的物理块数量)不小于工作集大小即可。
参考文献:
Linux内存管理机制(最透彻的一篇) - 知乎 (zhihu.com)内存管理(二)——连续分配管理方式_内存连续分配方式实验_愿天堂没有C++的博客-CSDN博客
清晰讲解Linux内核中连续内存分配与非连续内存分配(图例解析) - 知乎 (zhihu.com)操作系统内存管理——连续内存分配_如果os想将页面0对应的页框号修改为3_小鲸_hier的博客-CSDN博客
清晰讲解Linux内核中连续内存分配与非连续内存分配(图例解析) - 知乎 (zhihu.com)内存管理(二)——连续分配管理方式_内存连续分配方式实验_愿天堂没有C++的博客-CSDN博客
Linux内存管理机制(最透彻的一篇) - 知乎 (zhihu.com)
操作系统-------------------内存空间的分配方式(连续分配和非连续分配和虚拟存储技术)_ZJE_ANDY的博客-CSDN博客
Linux内存管理机制(最透彻的一篇) - 知乎 (zhihu.com)