当前位置:   article > 正文

操作系统---内存_操作系统内存

操作系统内存

目录

程序运行的机制

程序的运行方法---指令识别

逻辑地址和实际地址

写程序到程序运行

装入的三种方式,地址转换

链接的三种方式

 内存管理

 内存保护

设置上、下限寄存器

设置重定位寄存器和界地址寄存器

内存空间的分配与回收

连续分配方式

1、单一连续分配方式

2、固定分区分配

3、动态分区分配

 操作系统如何选择空闲区来给进程分配空间---动态分区分配算法

1、首次适应算法

2、最佳适应算法

3、最坏适应算法

4、邻近适应算法(循环首次适应算法)

 5、内部碎片和外部碎片的概念

4、基本分页存储管理

页表

基本地址变化机制

具有块表的基本地址变化机制

两级页表

5、基本分段存储管理

6、段页式存储管理

  内存扩充

1、覆盖技术

2、交换技术

3、虚拟内存技术

 虚拟内存

 请求分页管理方式

页面置换算法

最佳置换算法(OPT)

先进先出置换算法(FCFO)

最近最久未使用置换算法(LRU)

时钟置换算法(CLOCK)

改进后的时钟置换算法(CLOCK)

页面分配策略

驻留集

 调入页面的时机

从何处调入页面

页面抖动(颠簸)

工作集


内存:是存放数据的硬件,所有程序运行时都是要加载进入内存,程序在内存中才可以被CPU处理

内存有一个,但是程序不一定只有一个,多个程序的数据存储在一块内存中,如何确定这个程序的数据在内存的哪里存放呢?

存储单元:类似于酒店的房间,我们可以给内存划分成一个个的房间,这些房间就是存储单元

内存地址:为了方便管理,酒店房间会有编号,相应的,内存的存储单元也可以有编号,这个编号就是地址

每一个地址对应一个存储单元

程序运行的机制

程序的运行方法---指令识别

逻辑地址和实际地址

编译时期,程序数据的实际存放地址是未知的,我们知道的只会是逻辑地址

例如,逻辑上x的存放地址是0,但是实际存放中0的位置被占有了,那么x就要后移到1,地址1就是实际地址,0就是逻辑地址

写程序到程序运行

编译: 程序员写好的代码文件会经过编译,形成机器可以识别的二进制语言,这些二进制语言打包在目标文件里(c语言是.o文件,java就是.class文件),这些目标文件有自己独立的逻辑地址

链接:链接就是将编译后形成的一组目标文件和其所需的库函数等资源打包到一起,形成一个完整的装入模块,这个装入模块有自己独立的逻辑地址

装入:装入就是将链接形成的装入模块放入内存(逻辑地址到物理地址的转化)

装入的三种方式,地址转换

1、绝对装入

在装入之前,就已经知道程序要在内存中存放的绝对地址

 但是,绝对装入只有在单道程序环境下使用,因为这时内存只会有一个程序,从而可以知道内存可以装入代码的起始地址,才可以知道其实际地址

2、静态重定位

装入时就会实现逻辑地址到物理地址的转化,装入模块会根据内存的实际情况,自动调整装入位置

 静态重定位会让内存给作业分配全部空间,如果空间不够,就不能装入作业

作业在装入之后,地址空间就不会改变了

3、动态重定位(现代操作系统)

只有程序运行时,才会将逻辑地址转化为物理地址

需要一个重定位寄存器实现

 允许作业在内存中移动 (作业的重定位寄存器的值更改,那么作业的物理地址也会改变)

链接的三种方式

1、静态链接

将目标文件和所需的库函数连接称为一个完整的装入模块(形成一个完整的逻辑地址)之后不再拆开,装入程序将装入模块放入内存

2、装入时动态链接

将目标文件和所需的库函数一边装入内存,一边进行连接

 3、运行时动态链接

程序运行时需要哪一个目标文件,就选择这个目标文件放入内存并链接

 内存管理

操作系统肯定要管理内存,那么操作系统需要完成哪些工作呢?

1、操作系统负责内存的分配与回收:程序想要运行,就需要加载进入内存,那么操作系统如何知道给这个程序分配哪一块的空间呢?程序运行结束,要如何释放程序所占用的内存空间呢?

2、操作系统需要提供技术来从逻辑上扩充内存空间:一个游戏,内存可能有20G,电脑才有4G的内存,这个游戏要运行,肯定要加载进入内存,但是20G的大小是如何放入4G内存的呢?这就要涉及到虚拟技术

3、操作系统要实现逻辑地址到物理地址的转换:三种装入方式

4、操作系统要实现内存保护:操作系统要保证一个进程不会访问到别的内存空间

 内存保护

设置上、下限寄存器

1、设置上、下限寄存器存储进程物理地址的开始和结束位置(针对于物理地址的判断)

设置重定位寄存器和界地址寄存器

2、设置重定位寄存器和界地址寄存器(针对于逻辑地址的判断)

重定位寄存器:存储物理地址的起始位置;界地址寄存器:存放最大的逻辑地址

通过界地址寄存器,可以判断请求访问的逻辑地址是否越界,根据重定位寄存器可以从逻辑地址得到物理地址

内存空间的分配与回收

连续分配方式

产生连续的内存空间

1、单一连续分配方式

单一连续分配方式中,内存被分为系统区(低地址部分,存放操作系统相关数据)和用户区(高地址部分,存放用户进程数据)

内存中同一时刻只能有一道用户程序,这个程序独占用户区

优点:实现简单;可以利用覆盖技术实现扩充内存

缺点:空间浪费,只支持单道程序系统

2、固定分区分配

将内存划分为固定大小的分区,每一个分区只能装入一道作业

根据分区大小是否相等,可以分为:分区大小相等和分区大小不相等

分区大小相等:那么如果占有空间很小的进程,会占用一个分区,每个分区的大小过大,会造成内存空间的浪费(内部碎片),每个分区的大小过小,大进程又装不下

分区大小不相等:相比上一个,增加空间设置的灵活性

操作系统如何管理这些分区呢?

创建一个数据结构---分区说明表,如下所示:

3、动态分区分配

根据进程的空间要求,动态的划分内存空间

 1、操作系统如何管理这些分区呢?

 2、操作系统如何选择空闲区来给进程分配空间---动态分区分配算法

1、首次适应算法

原则:按照地址递增的顺序,找到第一个可以满足空间要求的空闲分区

实现:空闲分区表按照地址递增的顺序存储

缺点:每次都要从第一个位置开始查找,查找效率低下,产生外部碎片

2、最佳适应算法

原则:找到满足空间要求且空闲分区大小最小的空闲分区

实现:空闲分区表按照容量递增的顺序存储

 缺点:每次选择的都是最小的可以满足要求的空闲分区,产生外部碎片

3、最坏适应算法

为了解决最佳适应算法的问题而提出了最坏适应算法

原则:找到满足空间要求且空闲分区大小最大的空闲分区

实现:空闲分区表按照容量递减的顺序存储

 缺点:不断分裂大分区,导致大进程没有可以被存储的空间

4、邻近适应算法(循环首次适应算法)

为了解决首次适应算法查找速度慢的问题而提出

原则:按照地址递增的顺序,找到第一个可以满足空间要求的空闲分区

实现:空闲分区表按照地址递增的顺序存储,为了提高效率,可以从上一次查找结束之后的位置开始查找

缺点:可能分裂大分区,导致大进程没有可以被存储的空间

 内部碎片和外部碎片的概念

内部碎片:一个分区内,分配给某一个进程的空间没有用完

外部碎片:内存内,某些区间太小而无法使用

动态内存分配:没有内部碎片,但是会有外部碎片

解决办法:紧凑法:移动进程空间,腾出大的空闲空间 

3、操作系统如何分配和回收空间

将连续的空闲区合并,避免空间浪费


以上的3种方法分配的都是连续的内存空间,称之为连续分配管理方式,但是,固定区间分配会产生内部碎片,动态区间分配会产生外部碎片

那我们是不是可以在内存中给作业申请非连续空间来解决碎片问题呢?这个称之为非连续分配管理方式,或者称为离散分配方式

4、基本分页存储管理

以上的这个思想就是基本分页存储管理的实现原理:

内存划分为大小相等的分区,每一个分区称之为“页框”,每一个页框会有一个编号(页框号),编号从0开始

用户进程划分为一个个大小和页框相等的区域,每一个区域称之为:”页“,每一个页会有一个编号(页号),编号从0开始

操作系统会以页框为单位给进程分配空间,用户进程的每一个页面进入内存的每一个页框中

页框不能太大,否则会产生大的内部碎片

如何装入:实现逻辑地址到物理地址的转化?

为了计算机的方便,我们会将页面大小,设置为2的整数幂大小

使用32个二进制存储,那么前面20位(红色)部分对应的是页号,后12位(黑色)表示页内偏移量

也就是说,逻辑地址包括两部分:页号和页内偏移量;一个页面(页号固定)的大小由页内偏移量决定:一个页面包含的是n个逻辑地址,这个n=2^k(k是页内偏移量的二进制位数);一个进程的页面个数由页号决定

如果有k位表示页内偏移量(页地址数),那么系统的一个页面大小2^k

如果有M位表示页号,那么一个进程最多有页面2^M个

1、页号:逻辑地址/页面长度(80/50=1)

2、偏移量:逻辑地址%页面长度(80%50=30)

3、起始物理地址:操作系统需要提供某一种数据结构来组织存储各个页面的起始物理地址

4、物理地址=起始物理地址+偏移量

页表

起始物理地址:操作系统需要提供某一种数据结构来组织存储各个页面的起始物理地址,这个数据结构是页表

 先计算出逻辑空间对应的页号,在页表内查询到块号,这个块号对应着内存的页框号,根据页框号*内存给每个页框设置的固定长度,就可以得到起始物理地址

基本地址变化机制

基本地址变化机制借助页表,实现逻辑地址到物理地址的转化 

设置页表寄存器这一个结构,用于存储页表在内存的起始地址和页表长度,在进程没有执行时,页表在内存的起始地址和页表长度(页表有多少页)存放在PCB中,进程一旦开始调度,页表在内存的起始地址和页表长度,就会存储在页表寄存器中

1、进程没有被调度,PCB存储进程的信息,包括页表在内存的起始地址和页表长度

2、进程被调度,PCB将内存的起始地址和页表长度信息交给页表寄存器

3、根据逻辑地址计算得到页号(逻辑地址/页表内存大小)和页内偏移量(逻辑地址%页表内存大小)

4、页表寄存器根据页表长(页表数目的多少)判断是否查询越界,没有越界,那么由于每一个页表的长度是固定的且页表号是递增的,根据页表在内存的起始地址和页号可以得到逻辑地址所在页面的起始物理地址,根据页内偏移量最终得到实际的物理地址

5、给定逻辑地址就可以决定物理地址,地址是一维的

具有块表的基本地址变化机制

1、局部性原理

时间局部性:一个指令执行后,不久后被再次访问;一个数据被访问过,不久后这个数据被再次访问

空间局部性:一个程序访问过某一个存储单元,不久后,这个存储单元的附近存储单元也被访问

 由于局部性原理,极有可能一直访问的都是同一个页面,那么就会造成页表的多次不必要的查询,那么如何增加效率呢?为了解决这个问题,引入了快表机制

这就借助了高速缓冲的思想:将近期频繁使用的数据放到更高一级的存储器中

2、快表

快表,又可以称为联想寄存器,简称(TLB),是一种访问速度比内存快的高度缓冲存储器,用于存放当前访问的若干页表项,来加速地址转化的速度;与此对应,内存中页表的访问速度慢,称为慢表

 如果命中快表,可以不必对慢表进行访问,只需要计算出实际地址访问内存

 如果没命中快表,需要对慢表进行访问,同时将信息记入快表,计算出实际地址访问内存

3、两级页表

对于上面提供的单级页表这一数据结构,有两个缺点:1、单级页表要在内存中连续存储:根据页表号计算当前页表号对应的起始地址,就要求页表号是递增的,连续存储的;一旦这个页表内存申请过大,就不太容易在内存中找到连续且足够大的空间存储页表,况且这就失去了离散分配方式的意义2、单极页表的全部信息可能只需要那么几行,不需要全部存储在内存,浪费内存空间

为了解决这两个问题,提出了两级页表

如何解决问题一:参考解决进程连续分布的方法:将进程地址分页,建立页表,离散存储进程;那么可以将连续的页表分页,再次建立页表(称为页目录表或者外层页表),离散存储连续的页表

1、将连续的页表分页

 2、再次建立页表(称为页目录表或者外层页表),离散存储连续的页表

 如何解决问题二:在需要访问页面时,才将这个页面其调入内存;在页表项中增加标志位,用于标志这个页面是否进入内存

1、采用多级页表机制,每一级页表的大小不能超过一个页面

5、基本分段存储管理

进程的地址空间:根据程序的自身逻辑关系划分为多个段,每段从0开始编址

内存分配原则:根据段来分配,段之间连续分配,段和段之间离散分配

 分段系统的逻辑结构:由段号和段内偏移量构成

如果有k位表示段内偏移量,那么系统的一个段大小2^k

如果有M位表示段号,那么一个进程最多有段2^M个

段表:记录段存入内存的信息 

和页表相比,页表中每一个页的长度是系统的,一个段的长度是不一定的,需要显示记录段长

段表结构:

段表实现逻辑地址到物理地址的转化: 

 分页,分段管理的对比:

1、页是信息在物理上的存储,实现分页管理,主要就是实现离散分配,提高内存利用率。对用户不可见

2、段是信息的逻辑存储,实现分段管理,主要就是更好的服务用户,满足用户需求,一个段通常包含一个逻辑模块的信息,用户编程时要提供段名,对用户可见

 3、页的大小是固定的,由系统决定;段的大小不固定,取决于一个模块的信息,由用户编写的代码决定

4、分页管理,用户只需要给定一个地址就可以,地址是一维的;分段管理,需要用户给定地址和段名,地址是二维的

5、分页管理,产生内部碎片无外部碎片;分段管理,产生外部碎片无内部碎片

6、分页比分段更容易实现对于信息的共享和保护

6、段页式存储管理

先将用户进程分段,后分页

 逻辑地址结构由段号,页号,页内偏移量组成

段表、页表结构:

实现逻辑地址到物理地址的转化: 

 

  内存扩充

所谓的内存扩充,并不是指增加系统的物理内存,而是指在现有内存的基础上增加内存的使用率

1、覆盖技术

覆盖技术的思想:将程序分为多个段(模块),常用的段常驻内存,不常用的段就可以调出内存

内存将分为“固定区”和“覆盖区”

固定区:存放常用的段,程序运行期间内不会调出

覆盖区:存放不常用的段,程序运行期间内需要的时候调入,不需要的时候调出

缺点:程序的调用(覆盖)结构需要程序员自己声明,操作系统有了覆盖结构之后才能实现覆盖;对用户不透明,增加了用户的编程负担

2、交换技术

当系统的内存空间紧张的时候,系统可以将某些进程暂时调出内存,存入外存,也可以将外存中具备运行条件的进程换入内存(进程在内存和磁盘的交换)

处理机调度中的中级调度!!!

进程的PCB常驻内存

1、外存的哪些地方存放被换出的进程

具有对换功能的操作系统,会将外存(磁盘)分为两个区域:对换区和文件区;文件区用于存放文件,要求提高存储空间的利用率,采用离散分配方式管理文件区,对换区用于存放被内存换出的进程,主要追求换入换出的速度,采用连续分配方式;总之,对换区的I/O速度快于文件区

2、什么时候进程交换

换出一般出现于内存不足时,换入发生在内存空间充足且外存中具备运行条件的进程才可以换入

3、可以换出哪些进程

例如:优先级低的进程,阻塞的进程等

3、虚拟内存技术

 

 虚拟内存

根据局部性原理,我们可以在装入程序时,内存装入很快需要的部分,暂时不用的数据就留在外存

操作系统在程序运行期间,负责数据的换入换出,在用户看来,自己使用的内存很多,这就是虚拟内存

虚拟内存的最大容量和实际容量

虚拟内存的最大容量由计算机的地址结构决定

虚拟内存的实际容量是(内存+外存)和最大容量的最小值

使用虚拟内存之后,呈现:1、多次性:一个作业可以被分为多次调入内存2、对换性:操作系统在程序运行期间,负责数据的换入换出3、虚拟性:在用户看来,自己使用的内存很多,这就是虚拟内存

虚拟内存技术基于离散分配方式!!!

 请求分页管理方式

 在基本分页管理方式上的拓展

请求分页系统建立在基本分页系统基础之上,为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。请求分页是目前最常用的一种实现虚拟存储器的方法。

在请求分页系统中,只要求将当前需要的一部分页面装入内存,便可以启动作业运行。在作业执行过程中,当所要访问的页面不在内存时,再通过调页功能将其调入,同时还可以通过置换功能将暂时不用的页面换出到外存上,以便腾出内存空间。


为了实现请求分页,系统必须提供一定的硬件支持。除了需要一定容量的内存及外存的计算机系统,还需要有页表机制、缺页中断机构和地址变换机构。

页表结构

 缺页中断机制

当想要访问的页面不在内存时,需要请求将页面调入内存;这时会产生缺页中断,将进程加入阻塞队列,直到缺页中断程序处理完成,将页面调入内存,进程才会被唤醒,进入就绪队列

1、如果内存中有空闲位置,那么将页面加载进入内存

2、如果内存中没有空闲位置,使用页面置换,淘汰一个页面(如果这个页面的数据被修改过,那么数据要写回外存),然后将页面调入内存

缺页中断作为中断同样要经历,诸如保护CPU环境、分析中断原因、转入缺页中断处理程序、恢复CPU环境等几个步骤。但与一般的中断相比,它有以下两个明显的区别:

  • 在指令执行期间产生和处理中断信号,而非一条指令执行完后,属于内部中断。
  • 一条指令在执行期间,可能产生多次缺页中断。

地址变换机构

请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存,又增加了某些功能而形成的。

 在进行地址变换时,先检索快表:

  • 若找到要访问的页,便修改页表项中的访问位(写指令则还须重置修改位),然后利用页表项中给出的物理块号和页内地址形成物理地址。
  • 若未找到该页的页表项,应到内存中去查找页表,再对比页表项中的状态位P,看该页是否已调入内存,未调入则产生缺页中断,请求从外存把该页调入内存。

页面置换算法

 页面置换算法决定将哪一个页面换出外存

页面的换入换出需要磁盘I/O,好的页面算法应该减少磁盘I/O的次数

缺页率=(缺页次数)/页面访问次数

最佳置换算法(OPT)

淘汰的页面是以后不可能使用或者长时间不再使用的页面

 缺页率:9/20=0.45

系统无法知道并发情况下,进程的执行顺序,最佳置换算法无法实现!!!

先进先出置换算法(FCFO)

每次淘汰最早进入内存的页面

 缺页率:9/12=0.75

belady异常

最近最久未使用置换算法(LRU)

淘汰最近最久未使用页面

使用页表的访问字段记录从上一次访问到现在的时间

 逆向观察

  缺页率:6/20=0.3

效率好,但是实现困难

时钟置换算法(CLOCK)

使用页面的访问位,访问位为1表示最近访问过,访问位为0表示最近没有访问过

将内存的页面构造成循环队列

1、一个页面被访问,访问位就要置为1

2、需要淘汰一个页面,访问循环队列,找到访问位是0的页面换出,换入的页面的标志位是1;

 如果访问到的访问位是1,将其改为0,继续访问下一个页面

 性能和开销相对均衡

改进后的时钟置换算法(CLOCK)

之前说的时钟置换算法,只关注是不是被访问过;然而,如果被淘汰的页面没有在内存中被修改过,是不需要写回外存的

在其他条件相同时,优先淘汰没有修改过的页面

访问位为1表示最近访问过,访问位为0表示最近没有访问过

修改位是1,表示在内存中修改过,修改位是0,表示在内存中没修改过 

页面分配策略

驻留集

请求分页管理系统中,给进程分配的物理块的集合

采用了虚拟存储技术的系统,驻留集大小一般小于进程总大小

 固定分配:系统给每个进程分配固定数目的物理块,进程运行期间不会改变,也就是驻留集不变

可变分配:系统给每个进程分配一定数目的物理块,进程运行期间可以增加或者减少,也就是驻留集可变

局部置换:发生缺页,只可以选择进程自己的物理块进行置换

全局置换:发生缺页,操作系统可以将空闲的物理块分配给进程,也可以将别的进程的物理块置换到外存后分配给进程

现代操作系统通常釆用三种策略:

  1. 固定分配局部置换。它为每个进程分配一定数目的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后再调入需要的页面。实现这种策略难以确定为每个进程应分配的物理块数目:太少会频繁出现缺页中断,太多又会使CPU和其他资源利用率下降。
  2. 可变分配全局置换。这是最易于实现的物理块分配和置换策略,为系统中的每个进程分配一定数目的物理块,操作系统自身也保持一个空闲物理块队列。当某进程发生缺页时,系统从空闲物理块队列中取出一个物理块分配给该进程,并将欲调入的页装入其中。
  3. 可变分配局部置换。它为每个进程分配一定数目的物理块,当某进程发生缺页时,只允许从该进程在内存的页面中选出一页换出,这样就不会影响其他进程的运行。如果进程在运行中频繁地缺页,系统再为该进程分配若干物理块,直至该进程缺页率趋于适当程度; 反之,若进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块。

可变分配全局置换和可变分配局部置换的区别:

可变分配局部置换,根据缺页频率动态调整物理块数目;可变分配全局置换,只要发生缺页就会增加物理块

 调入页面的时机

为确定系统将进程运行时所缺的页面调入内存的时机,可釆取以下两种调页策略:

  1. 预调页策略。根据局部性原理(空间局部性),一次调入若干个相邻的页可能会比一次调入一个页面更高效。但如果调入的一批页面中大多数都未被访问,则又是低效的。所以就需要釆用以预测为基础的预调页策略,将预计在不久之后便会被访问的页面预先调入内存。但目前预调页的成功率仅约50%。故这种策略主要用于进程的首次调入时,由程序员指出应该先调入哪些页。
  2. 请求调页策略。进程在运行中需要访问的页面不在内存而提出请求,由系统将所需页面调入内存。由这种策略调入的页一定会被访问,且这种策略比较易于实现,故在目前的虚拟存储器中大多釆用此策略。它的缺点在于每次只调入一页,调入调出页面数多时会花费过多的I/O开销。

从何处调入页面

请求分页系统中的外存分为两部分:用于存放文件的文件区和用于存放对换页面的对换区。对换区通常是釆用连续分配方式,而文件区釆用离散分配方式,故对换区的磁盘I/O速度比文件区的更快。这样从何处调入页面有三种情况:

  1. 系统拥有足够的对换区空间:可以全部从对换区调入所需页面,以提髙调页速度。为此,在进程运行前,需将与该进程有关的文件从文件区复制到对换区。
  2. 系统缺少足够的对换区空间:凡不会被修改的文件都直接从文件区调入;而当换出这些页面时,由于它们未被修改而不必再将它们换出。但对于那些可能被修改的部分,在将它们换出时须调到对换区(读写速度快),以后需要时再从对换区调入。
  3. UNIX方式:与进程有关的文件都放在文件区,故未运行过的页面,都应从文件区调入。曾经运行过但又被换出的页面,由于是被放在对换区,因此下次调入时应从对换区调入。进程请求的共享页面若被其他进程调入内存,则无需再从对换区调入。

页面抖动(颠簸)

刚刚换出的页面马上又要换入主存,刚刚换入的页面马上就要换出主存,这种频繁的页面调度行为称为抖动,或颠簸。
页面抖动的主要原因是某个进程频繁访问的页面数目高于可用的物理块数目。

工作集

工作集是指在某段时间间隔内,进程要访问的页面集合。

驻留集是指在请求分页管理系统中给进程分配的内存块的集合

经常被使用的页面需要在工作集中,而长期不被使用的页面要从工作集中被丢弃。为了防止系统出现抖动现象,需要选择合适的工作集大小。


 

一般来说:驻留集大小>=工作集大小,否则进程在运行中会频繁发生缺页

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/687591
推荐阅读
相关标签
  

闽ICP备14008679号