操作系统——内存

本文深入探讨了操作系统的内存管理,包括内存的逻辑地址与物理地址转换、装入方式(绝对装入、静态重定位、动态重定位)、链接方式(静态链接、装入时动态链接、运行时动态链接)以及内存保护机制。还详细介绍了内存连续分配和离散分配策略,如单一连续分配、固定分区分配、动态分区分配,并讨论了各种分配算法的优缺点。最后,文章提到了分页和分段技术,以及段页式管理方式在解决内存碎片问题上的作用。

内存概述

内存是存放进程的存储器,可以按字节编址或按字编址。内存种存放进程的地址是物理地址,但一般不会在程序种使用物理地址,而是使用逻辑地址,在把进程装入内存时,需要把逻辑地址转化成物理地址。

地址:逻辑地址(相对地址),物理地址(绝对地址)
从源代码到变成进程运行:
编译:由源代码生成目标模块,把高级语言翻译成机器语言。
链接:由目标模块生成装入模块,形成完整的逻辑地址。
装入:把装入模块装入内存, 形成物理地址。

装入方式:

  • 绝对装入:编译器负责地址转换,产生绝对地址。;装入程序按装入模块中的地址存储代码和数据。只适用于单道程序。
  • 静态重定位:又称可重定位装入,装入程序负责地址转换。装入模块的地址是从0开始的逻辑地址,由装入程序“重定位”,转化为物理地址,将模块存入到内存中合适的位置。用于早期的多道程序计算机。
  • 动态重定位:又称动态运行时装入,运行后才进行地址转换。装入内存后仍然是逻辑地址,运行时才转化为物理地址,需要设置一个重定位寄存器,存放存放装入模块的起始地址。这样,程序就可以靠部分代码运行起来,运行时可以动态申请内存。应用于现代操作系统。

链接方式:

  • 静态链接:把整个模块装入内存。
  • 装入时动态链接:边装入边链接。
  • 运行时动态链接:在需要时才链接模块。

内存保护:
设置上、下限寄存器,比如逻辑地址为0-99,物理地址是从10开始,那么上限就是110,下限就是10,
设置重定向寄存器和界地址寄存器,前者存放起始物理地址,后者存放最大逻辑地址。

内存空间如何扩充:
覆盖技术:内存分为一个固定区和若干个覆盖区,程序代码按功能划分成几段,常用的段放在固定区,调入后就直到运行结束才调出。不常用的放在覆盖区,使用时才调入内存,不使用就调出内存。只适用于早期操作系统,已称为历史。
交换技术:就是中级调度,一般外存会分为对换区和文件区,对换区就是用于存储挂起的进程,采用连续存储的方式,I/O速度比离散存储的文件区快。

内存连续分配方式

在内存分配的过程中,可能会出现内部碎片,即存放进程的内存区域内有没有利用的空间,或外部碎片,即内存中某些空闲分区因为太小,放不下任何程序而被浪费。
内存分配策略:

  • 单一连续分配:只支持单道程序,内存被分为系统区和用户区。系统区的用户区的全部空间只用来放一个程序。不存在外部碎片,存在内部碎片。只适用于单道程序计算机
  • 固定分区分配:内存中划分出几个固定的区间,进程按大小放入不同的区间,区间的大小可以相同也可以不同。如果一个程序太大,放不进任何区间,就使用覆盖技术。不存在外部碎片,存在内部碎片。如果程序太大,就得使用覆盖技术,会降低性能。分区大小相同的适用于控制多个相同对象的计算机,如控制流水线的计算机。
  • 动态分区分配:内存中事先没有划好空间,在进程运行后,按动态分区分配算法放入空闲分区里。存在外部碎片,不存在内部碎片。

动态分区有两种存储分区地址的方式:

  • 动态分区表:一张表格,存放空闲分区的序号、大小、起始位置
  • 动态分区链:空闲分区的相关信息存储在节点里。

动态分区的分配:

  • 如果是从一个空闲分区里分出进程的空间,那么就减少这个分区的大小。
  • 如果占用整个空闲分区,那么就删除这个空闲分区。

动态分区的回收:

  • 如果回收后,存在相邻的两个空闲分区,那么就合并两个分区和被回收的分区。
  • 如果回收后,只存在相邻的一个空闲分区,那么就合并到这个分区里。
  • 如果没有相邻的空闲分区,就新建一个空闲分区。

动态分区分配算法:

  • 首适应算法:每次从内存的首地址开始寻找空闲分区,找到合适大小的就分配给进程,需要空闲分区以按地址大小以递增顺序存储在动态分区表/链里。优点:综合效果最好;开销小。缺点:会产生大量的碎片。
  • 最佳适应算法:优先使用较小的内存分区,为体积比较大的进程腾出大分区,需要空闲分区按容量大小以递增顺序存储在动态分区表/链里。优点是有利于存放大大进程,缺点:每次回收内存后都要重新对空闲分区排序,开销大;产生许多外部碎片。
  • 最坏适应算法:优先使用较大的内存分区,需要空闲分区按容量大小以递减顺序存储在动态分区表/链里。优点:不会产生太多外部随拍。缺点:容易造成大进程所需空间不足。
  • 邻近适应算法:从上次查找结束的位置开始查找,找到合适大小的就分配给进程,需要空闲分区以按地址大小以递增顺序存储在动态分区表/链里,可排列成循环链表。优点:不用每次从低地址开始查找;开销小。缺点:高地址的大空间可能会被使用,从而无法存放大进程。

内存离散分配方式

为了避免产生太多的碎片,我们可以采用离散的内存分配方式,将内存空间均分成一个个存储单元,即分页,存放进程时,把进程分开存放到这些存储单元里。这些存储单元是页框(页帧,内存块、物理块),编号叫页框号(页帧号,内存块号、物理块号),进程存放在存储单元的代码被称为页面,页面和页框是一一对应的关系,为了方便计算,页面大小通常是2的整数幂。

从逻辑地址转换为物理地址:

  • 计算逻辑地址对应的页号,页号=逻辑地址/页面大小
  • 找到页号所在的物理地址
  • 计算页内偏移量,偏移量=逻辑地址 % 页面大小
    物理地址 = 页号+业内偏移量

页表存放页面在内存里的物理地址,每一个进程对应一张进程表,页表的每一个页表项对应一个页面,包括页号+块号。页表项的长度相同,在内存里以连续方式存放,因此知道了页表存放起始地址和页表项的长度,就知道页号,因此页号是隐含的。

有些页面需要多次访问,那么就可以把页号和块号存放在快表里,原来的页表称为慢表。如果访问快表找到了信息,称为命中,如果没有就访问慢表。访问快表的速度通常要快于慢表,而且命中率在90%以上。

单级页表存在的问题:

  • 单个页表可能很大,需要占用很大的连续空间。
  • 有些页面不经常访问,可以不用存在内存里。

两级页表:

  • 顶级页表,即页目录表,负责存储二级页表的地址,放在PCB里。
  • 下一级的页表可以有多个,不常用的存在外存里。
    如果没有在内存里发现二级页表,就进行缺页中断,从外存里调入内存。

两级页表的地址转换:
逻辑地址分为三个部分:一级页号,二级页号,页内偏移量。
先在页目录表里找到二级表的地址,再在二级表里找到物理地址。

多级页表:

  • 逻辑地址的位数减去页面大小的w位数,剩下就是页号的位数。
  • 用页面大小除页表项长度,得到的就是一个页表最多能存多少个页面。
  • 再用页号的好位数整除,就可以判断需要多少级的页表。
    确定逻辑地址的结构。
    举例:
    某计算机按字节寻址,支持40位的逻辑地址,页面大小位4kb,页表项长度位4b。
    至少分三级。

从程序员的角度,我们可以把整个程序分成不同的逻辑模块,然后离散地存放在内存里,这被称位段。每个段的长度不一样,必须要给出段名。段的逻辑地址包括段号和段内逻辑地址,段表类似于页表,存储段长和段的基始地址,段号和页号一样是隐含的。段表项的长度是一样的,位数由逻辑地址中段号的位数和内存的位数确定,比如内存是4GB即32位,逻辑地址的段长的位数为16位,因此段表项的长度位16+32=48bite,即6B。

分段与分页的对比:

  • 段的地址空间是二维,页的地址空间是一维的。、
  • 段是信息的逻辑单位,页是信息的物理单位。
  • 按逻辑分,可以对不可修改的代码标注,但按页分就不行。
  • 段会产生外部碎片,页不会。

段页管理方式:先分段再分页。段号决定了程序可以分成几段,页号决定了一个段可以分成几页。逻辑地址分为段号,页号,页内偏移量。只有段号是用户可见的,其他由系统给出。

地址转换:先通过段号找到对应的页表,再通过页表找到地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值