【操作系统】第三章:内存管理

第三章:内存管理


一、内存管理

1.内存管理内容
(1)地址转换:

image-20220924075118384

Point1:程序装入

绝对装入:在编译时如果知道程序将放入到内存中的哪个位置,编译程序将产生绝对地址的目标代码。装入程序按照装入模块中的地址,将程序的数据装入内存。

image-20220924072557250

可重定位装入(静态重定位):根据内存的目前情况将装入模块装入到内存的适当位置(装入时对地址进行重定位),地址变换是在装入时一次完成的。

image-20220924073124235

动态运行时装入(动态重定位):装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行(装入内存后所有的地址依然是逻辑地址),这种方式需要一个重定位寄存器的支持。

image-20220924074040911

动态重定位优点:

  1. 允许程序在内存中发生移动。
  2. 可将程序分配到不连续的存储区中
  3. 在程序运行前只需装入部分代码即可投入运行
  4. 在程序运行期间,可根据需要动态申请分配内存空间
  5. 便于程序段的共享
  6. 可向用户提供一个比存储空间大得多的地址空间
Point2:程序链接

静态链接:在程序运行之前,先将各目标模块所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。

image-20220924084723863

装入时动态链接:将各目标模块装入内存时,边装入边链接的方式。

image-20220924084939171

运行时动态链接:在程序执行中需要该目标模块时才对其进行链接,这种方式便于修改和更新,便于实现对目标模块的共享。

image-20220924085235225

(2)内存保护:

上下限寄存器:在CPU中设置一对上下限寄存器,存放进程的上、下限地址。进程的指令要访问某个地址时,CPU检查是否越界。

image-20220924110023534

重定位寄存器:采用重定位寄存器(基地址寄存器)和界地址寄存器(限长寄存器)进行越界检查。重定位寄存器中存放的是进程的起始物理地址,界地址寄存器中存放的是进程的最大逻辑地址。

image-20220924110702272

(3)内存空间的扩充:
Point1:覆盖技术

覆盖技术:将程序分为多个段,常用段常驻内存不常用段在需要时调入内存,以解决程序大小超过物理内存总和的问题。

  • 常用段:放在内存的固定区中,调入后就不再调出(除非运行结束)
  • 非常用段:放在覆盖区,需要用到时调入内存,用不到时调出内存。

image-20220924112306127

Point2:交换技术

交换技术:内存空间紧张时,系统将内存中的某些进程暂时换出外存(进程的PCB保留在仍然内存中),把内存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)

image-20220924114542054

问题1:在外存的什么位置保存被换出的进程?

在具有交换功能的操作系统中,通常把磁盘分为文件区与对换区两部分。

  • 文件区:主要用于存放文件,追求存储空间的利用率(采用离散分配方式)
  • 对换区:主要用于存储被换出的进程,追求换入换出速度(采用连续分配方式)

问题2:什么时候进行进程交换?

交换通常在进程运行且内存吃紧的情况下发生,而当系统符合降低时就暂停。

例如:在发现许多进程运行时经常发生缺页,就说明内存紧张此时可以换出一些内存,而当缺页率下降时就可以暂停换出。

问题3:应该交换出哪些进程?

可优先换出阻塞的进程、优先换出优先级更低的进程、为了防止优先级低的进程饥饿,还会考虑进程在内存中驻留的时间问题。

【补充】覆盖与交换的区别

  1. 覆盖是在同一个程序或进程中进行的
  2. 交换是在不同的进程(或作业)之间进行的
(4)内存空间的分配与回收:

image-20220927072929670

2.连续分配管理方式
(1)单一连续分配:

在单一连续分配方式中,内存被分为系统区与用户区

  • 系统区:通常位于内存的低地址部分,用于存放操作系统相关数据。
  • 用户区:用于存放用户进程相关数据。
  • 内存中只能有一道用户程序,用户程序独占整个用户区空间。

image-20220924122030205

(2)固定分区分配:

将整个用户空间划分为固定大小分区,每个分区中装入一道作业,为支持多道程序系统在内存中装入多道程序(且互不干扰)。

image-20220924123215280

(3)动态分区分配:

不预先划分内存分区,而是在进程装入内存时根据进程的大小动态的建立分区,并使分区的大小正好适合进程的需要,因此系统分区的大小和数量是可变的。

问题1:操作系统用什么数据结构记录内存的使用情况?

答:空闲分区表、空闲分区链

image-20220924123916602

问题2:当有多个空闲分区满足需求时,应该选择哪个分区进行分配?

答:需要按照动态分区分配算法从空闲分区表或空闲分区链中选出一个分区分配该改作业,包含4中动态分区分配算法。

问题3:如何进行分区的分配与回收?

答:回收之后如果发现有一些空闲分区是相邻的,则需要对空闲分区进行合并。

注:关于内部与外部碎片

  1. 内部碎片:分配给某进程的内存区域中,有些部分没有用上
  2. 外部碎片:内存中的某些空闲分区由于太小而难以利用上。

image-20220924125341539

(4)动态分区分配算法:

首次适应算法First Fit

  • 算法思想:每次都从低地址开始查找,找到第1个能够满足大小的空闲分区。

  • 算法实现:空闲分区以地址递增的次序进行排列,每次分配内存时顺序查找空闲分区链or空闲分区表,找到大小能够满足要求的第1个空闲分区。

image-20220925105956412

最佳适应算法Best Fit

  • 算法思想:由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证大进程到来时能有连续的大片空间,可以尽可能多的留下大片的空闲区域,即优先使用更小的空闲区域
  • 算法实现:空闲分区按容量递增次序链接,分配内存时顺序查找空闲分区链or空闲分区表,找到大小满足要求的第1个空闲分区。

image-20220925111230304

BestFit缺点:每次都选取最小的分区进行分配,会留下越来越多很小的难以利用的内存块(外部碎片)。

最坏适应算法Worst Fit

  • 算法思想:为解决BestFit算法的问题,可以在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小。
  • 算法实现:空闲分区按照容量递减次序进行链接,每次分配内存时顺序查找空闲分区链or空闲分区表,找到大小满足要求的第1个空闲分区。

image-20220925111719745

邻接适应算法NextFit

  • 算法思想:首次适应算法每次都从链头开始查找,这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查找结束的位置开始检索就能解决这个问题
  • 算法实现:空闲分区以地址递增的顺序排列(可排成循环链表便于检索),每次分配内存时从上次查找结束的位置开始查找空闲分区or空闲链表,找到大小能够满足要求的第1个空闲分区。

注:邻接适应与首次适应算法与最佳/最坏算法相比,在空闲内存分区发生变化后,无需对整个链表进行重新排列(节省开销)。

首次适应算法每次都从头查找检索低地址的小分区,其更有可能把高地址部分的大空闲分区保留下来(最佳适应算法优点)

邻接适应算法无论高低地址部分的空闲分区都被相同概论的使用,导致大分区被划分为小分区、无大分区可用(最坏适应算法的缺点)

image-20220925115313809

注:综合来看四种算法中反而首次适应算法的空闲内存分区分配策略是最好的。

3.基本分页存储管理
(1)基本分页存储

内存空间分为大小相等的分区,每个分区就是一个页框/页帧/内存块/物理块/物理页面Page Frame,每个页框有一个页框号从0开始。

进程的逻辑地址空间也分为与页框大小相等的部分,每个部分称为一个页/页面Page,每个页面有一个页号从0开始。

操作系统以页框为单位为各进程分配内存空间,进程的页面与内存的页框有映射关系(每个页面不必连续存放)

image-20220927074257712

操作系统为每个进程建立一张页表(1个进程对应1张页表),以获取页面在内存中存放的位置(页框位置)

image-20220927074736519

★问题1:每个页表项占多少字节?

image-20220927075326885

★问题2:如何实现逻辑地址到物理地址的转换?

虽然进程的各个页面是离散存放的,但是页面内部是连续存放的,如果要访问逻辑地址A则:

  1. 确定逻辑地址A对应的页号P
  2. 确定逻辑地址A的页内偏移量W
  3. 找到P号页面在内存中的起始地址(通过查询页表)
  4. 逻辑地址A对应的物理地址 = P号页面在内存中的起始地址 + 页内偏移量W

问题3:如何确定一个逻辑地址对应的页号P 和 页内偏移量W?

image-20220927084820388

问题4:页面大小为2的整数次幂,为地址转换带来的好处?

  • 页号与页内偏移量的计算:如果页面大小为2kB,用二进制数表示逻辑地址则末尾k位即为页内偏移量,其余部分即为页号。

image-20220927091711672

  • 物理地址的计算:根据逻辑地址得到页号,根据页号在页表中找到页面存放的内存块号,将二进制表示的内存块号页内偏移量拼接起来,就可以得到最终的物理地址。

image-20220927091736737

  • 分页存储管理的逻辑地址结构如下:

image-20220927092320624

(2)基本地址变换机构

基本地址变换机构可以借助进程的页表将逻辑地址转换为物理地址。

通常会在系统中设置一个页表寄存器PTR,存放页表在内存中的起始地址F和页表长度M,

进程执行时页表始址F和页表长度M存在PCB中,当进程被调度时操作系统内核会将这两个参数放到页表寄存器中。

image-20220927101937966

设页面大小为L,将逻辑地址A转换到物理地址E的过程如下

  1. 计算页号与页内偏移量:根据逻辑地址A,划分得到页号P和页内偏移量W(如果是十进制需要手算P=A/L、W=A%L)
  2. 页号合法性检查:比较页号P与页表长度M,若P≥M则产生越界中断(特判P=M也会发生中断!)
  3. 计算页表项地址以获取内存块号:页表项地址=页表起始地址F+页号P*页表项长度,取出该页表项内容b即为内存块号
  4. 计算物理地址E:E=b*L+W
  5. 用得到的物理地址E去访问内存

image-20220927102338985

注:在分页存储管理系统,只要确定了每个页面的大小逻辑地址结构就确定了(页式管理中地址是一维的)

实际应用中通常使一个页框恰好能够放入整数个页表项

image-20220927103755569

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

具有快表的地址变换机构是对基本地址变换结构的改进

TLB联想寄存器,translation lookaside buffer是一种访问速度比内存快很多的高速缓存(非内存),用于存放最近访问页表项的副本。

TLB联想寄存器可以加速地址变换的速度,因此内存中的页表项常被称为慢表。

引入快表后地址的变换过程

image-20220927112540176

  1. CPU给出逻辑地址由硬件计算出页号、页内偏移量,将页号与块表中的所有页号进行比较。
  2. 如果找到匹配的页号,则直接从中取出该页对应的内存块号,再将内存块号与页内偏移量拼接成物理地址后访问对应的内存单元。
  3. 如果没有找到匹配的页号,则需要访问内存中的页表找到对应的页表项,得到页面存放的内存块号,再将内存块号与页内偏移量拼接成物理地址后访问对应的内存单元。(在找到页表项后应同时将其存入快表以便后续使用,若快表已满需按照一定的算法对旧的页表项进行替换)

image-20220927113438428

注:TLB和普通Cache的区别—TLB中只有页表项的副本,而普通Cache中可能会有其他的各种数据的副本

(4)两级页表

单级页表中存在需要改进的问题:

问题1:根据页号查询页表的方法,要求所有的页表项都连续存放(需要占用很多个连续的页框)

可将长长的页表进行分组,使每个内存块刚好可以放入一个分组,再将各分组离散的放到各个内存块中。另外要为离散分配的页表再建立一张页表,称为页目录表/外层页表/顶层页表。

image-20220928120425294

image-20220928120435825

两级页表实现逻辑地址转换:

  1. 按照地址结构将逻辑地址拆分成三个部分
  2. 从PCB中读出页目录表的起始地址,再根据一级页号查询页目录表,找到下一级页表在内存中的存放位置。
  3. 根据二级页号查表,找到最终想要访问的内存块号
  4. 结合页内偏移量得到物理地址

问题2:进程在一段时间内只需访问某几个特定的页面就可正常工作,没有必要让整个页表都常驻内存中

可以在需要访问页面时才将页面调入内存(虚拟存储技术),可以在页表项中添加一个标志位用于表示该页面是否已调入内存。如果想要访问的页面不再内存中,则产生缺页中断(内中断)然后将目标页面从外存中调入内存。

问题3:若采用多级页表机制,则各级页表的大小不能超过一个页面

image-20220928124821393

注:两级页表的分表使得访问内存的次数变多,具体访问次数为n级页面访问内存的次数为n+1次

4.基本分段存储管理
(1)基本分页存储

将进程的地址空间,按照程序自身的逻辑关系(程序可读性更高)划分为若干个段,每个段都有一个段名(每段从0开始编址)。

以段为单位进行内存的分配,每个段在内存中占据者连续的空间,但各段之间可以不相邻。

image-20220928130149729

为了能够从物理内存中找到各个逻辑段的存放地址,为此需要为各进程建立一张段映射表(段表)

image-20220928132445118

(2)基本地址变换机构

基本分段存储管理地址转换的过程

image-20220928140929488

(3)分段存储与分页存储对比:

image-20220928134830805

5.段页式管理

image-20220928135239068

综合分段分页管理的优点,提出了段页式内存管理

(1)段页式管理方式:

image-20220928135607785

image-20220928135727689

(2)段表页表:

image-20220928140004786

(3)地址转换:

image-20220928140822412

二、虚拟内存管理

1.虚拟内存

操作系统将内存中暂时用不到的信息换出外存,从而实现内存的虚拟扩充就是虚拟内存技术,虚拟内存的三个特征:

  • 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。
  • 对换性:在作业运行时无序一直常驻内存,而是允许在作业运行过程中将作业换入、换出。
  • 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存远大于实际的容量。

注:虚拟内存的实现需要建立在离散分配的内存管理方式基础之上。

2.请求分页管理方式

与传统的基本分页管理方式不同,在程序执行过程中

若所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序(请求调页/段)。

若内存空间不够时,由操作系统负责将内存中暂时用不到的信息换出到外存(页面/段置换)。

(1)页表机制:

image-20220929190608230

(2)缺页中断机构:

在请求分页系统中,每当要访问的页面不在内存时便产生一个缺页中断,然后由操作系统的缺页中断处理程序处理中断。此时缺页的进程阻塞,放入阻塞队列。在调页完成后再将其唤醒,放回就绪队列。

  • 如果内存有空闲块则为进程分配一个空闲块,将所缺页面装入该块,并修该页表中相应的页表项。
  • 如果内存中没有空闲块,则由页面置换算法选择一个页面淘汰。若该页面在内存期间被修改过,则要将其写回外存未修改的页面不用写回外存。
(3)地址变换机构:

请求分页存储管理与基本分页存储管理的主要区别:

  1. 请求调页(查到页表项时进行判断)
  2. 页面置换(需要调入页面,但没有空闲内存块时进行)
  3. 需要修改请求页表中新增的表项

image-20220929212124794

(4)页面置换算法:

页面换入/换出需要磁盘IO,会有较大的开销,因此好的页面置换算法应该追求更少的缺页率。

OPT最佳置换算法,Optimal:每次选择淘汰的页面是以后永不使用or在最长时间内不被访问的页面,可以保证最低的缺页率。

image-20220929215149579

注:最佳置换算法可以保证最低的缺页率,但实际上操作系统无法提前预判页面访问序列,因此OPT置换算法是无法实现的。

FIFO先进先出置换算法:每次淘汰的页面是最早进入内存的页面。

将调入内存的页面根据调入先后顺序排成队列,换出页面时选择队头页面,队列的最大长度取决于系统为进程分配的内存块数量。

image-20220929215313654

image-20220929215346399

注:FIFO会产生Belady异常,与进程实际运行时的规律不适应(先进入的页面也有可能是最进程被访问的)因此算法性能较差。

LRU最近最久未置换算法,least recently used:每次淘汰的页面是最近最久未使用的页面,

在每个页面页表项的访问字段中,记录页面自上次被访问以来所经历的时间t,需要淘汰页面时选择现有页面中t值最大的即可。

image-20220929220320078

注:LRU算法的实现需要专门的硬件支持,虽然算法性能好(最接近OPT)但是实现困哪、开销较大。

CLOCK时钟置换算法:最近未用算法NRU,Not Recently Used

为每个页面设置一个访问位,将内存中的页面都通过链接指针链接成一个循环队列。

image-20220929221219764

改进型的时钟置换算法

简单的时钟置换算法仅考虑了页面是否被访问过,

如果被淘汰的页面没有被修改过,则无需执行IO操作写回外存,只有被淘汰的页面被修改过时才需要写回外存。

因此除了考虑一个页面的访问、还应考虑是否被修改过,在其他条件都相同时应该优先淘汰没有被修改过的页面。

image-20220929222745608

(5)页面分配置换策略:

驻留集:请求分页存储管理中给进程分配的物理块/页框的集合。如果驻留集太小会导致缺页频繁,系统要花大量的时间来处理缺页,实际用于进程推进的时间很少。如果驻留集太大又会导致多道程序并发度下降,资源利用率降低。

页面分配策略:

  • 固定分配:驻留集大小不变,操作系统为每个进程分配一组固定数目的物理块,在进程运行期间不再改变。
  • 可变分配:驻留集大小可变,先为每个进程分配一定数目的物理块,在进程运行期间可根据情况适当做增加与减少。

页面置换策略:

  • 局部置换:发生缺页时只能选进程自己的物理块进行置换。
  • 全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,在分配给缺页进程。

image-20220930115120463

固定分配局部置换策略

  1. 系统为每个进程分配一定数量的物理块,在运行期间都不改变。
  2. 若进程在运行中发生缺页,则只能从该进程在内存中的页面中换出一页,然后再调入需要的页面。
  3. 缺点:很难在刚开始就确定应该为每个进程分配多少个物理块才算合理。

可变分配局部置换策略

  1. 刚开始系统为每个进程分配一定数量的物理块,操作系统会保持一个空闲物理块队列,
  2. 当某进程发生缺页时,从空闲物理块中取出一块分配给该进程。若已无空闲物理块则可选择一个未锁定的页面换出外存,再将该物理块分配给缺页的进程。
  3. 采用这种策略时,只要某进程发生缺页都将获得新的物理块,仅当空闲物理块用完时,系统才选择一个未锁定的页面调出。被选择调出的页面可能是系统中的任何一个进程的页,因此这个被选中的进程拥有的物理块会减少,缺页率会增加。

可变分配全局置换策略

  1. 刚开始会为每个进程分配一定数量的物理块,当某进程发生缺页时,只允许从该进程自己的物理块中选出一个进行换出外存。
  2. 如果进程在运行中频繁地缺页,系统会为该进程多分配几个物理块,直至该进程缺页率趋势当程度。
  3. 如果进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块。
  • 可变分配全局置换:只要缺页就给分配新物理块。
  • 可变分配局部置换:要根据发生缺页的频率来动态地增加或减少进程的物理块。

何时调入页面

  • 预调页策略:预测不久之后可能访问到的页面,将预先调入内存。该策略主要用于进程的首次调入,由程序员指出先调入的部分。
  • 请求调页策略:进程在运行期间发现缺页时才将所缺页面调入内存。该策略调入的页面一定会被访问到,但IO开销比较大。

何处调入页面

  • 系统拥有足够的对换区空间时

    页面的调入调出都是在内存与对换区之间进行的,从而保证页面的调入调出速度,

    在进程运行前需要将进程相关的数据从文件区复制到对换区。

  • 系统缺少足够的对换区空间时

    对于不会被修改的数据都直接从文件区调入,由于文件不会被修改因此换出时不必写回磁盘,下次需要时再从文件区调入即可。

    对于可能会被修改的部分,换出时需要写回磁盘对换区,下次需要时再从对换区调入。

  • UNIX方式:

    运行之前进程有关的数据全部放在文件区,故未使用过的页面都可从文件区调入。

    若被使用过的页面需要换出,则写回对换区,下次需要时从对换区调入。

image-20220930124929826

(6)抖动和工作集:
  • 抖动:刚换出的页面马上又要换入内存,刚换入的页面马上又要换出内存,这种频繁的页面调度行为称为抖动/颠簸。产生抖动的主要原因是进程频繁访问的页面数量高于可用的物理块数量(分配给进程的物理块不够)
  • 工作集:在某段时间间隔里,进程实际访问页面的集合。
  • 驻留集:请求分页存储管理中给进程分配的物理块/页框的集合

注:驻留集的大小一般不能小于工作集的大小,否则进程运行过程中将发生频繁的缺页。

image-20220930130711259

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
现代操作系统第四版是一本经典的操作系统教材,第三章主要讲解了进程的概念、进程控制块、进程状态以及进程调度等内容。以下是第三章的主要内容概述: 1.进程的概念:进程是程序在执行过程中分配和管理资源的基本单位,每个进程都有自己的地址空间、数据栈、指令计数器、寄存器和文件描述符等。 2.进程控制块:进程控制块是操作系统内核中用于管理进程的数据结构,包含了进程的状态、进程ID、优先级、程序计数器、寄存器、内存分配情况、打开文件列表等信息。 3.进程状态:进程状态包括运行态、就绪态、阻塞态和创建态等,进程在不同状态之间转换,操作系统根据进程状态来进行进程调度。 4.进程调度:进程调度是操作系统内核中的一个重要模块,负责决定哪个进程可以获得CPU的使用权,进程调度算法包括先来先服务、短作业优先、时间片轮转等。 5.进程同步:进程同步是指多个进程之间的协作,包括互斥、信号量、管程等机制,用于保证多个进程之间的正确性和一致性。 6.进程通信:进程通信是指多个进程之间的信息交换,包括共享内存、消息队列、管道等机制,用于实现进程之间的数据传输和共享。 以下是现代操作系统第四版第三章的相关代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t pid; int status; pid = fork(); if (pid < 0) { printf("Fork error\n"); exit(1); } else if (pid == 0) { printf("Child process\n"); exit(0); } else { printf("Parent process\n"); wait(&status); printf("Child exit status: %d\n", WEXITSTATUS(status)); } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值