导图社区 408操作系统第三章 内存管理 内存管理概念
“内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。一个执行中的程式,譬如网页浏览器在个人电脑或是图灵机(Turing machine)里面,为一个行程将资料转换于真实世界及电脑内存之间,然后将资料存于电脑内存内部(在计算机科学,一个程式是一群指令的集合,一个行程是电脑在执行中的程式)。
编辑于2022-08-19 17:00:58 黑龙江省第三章 内存管理 3.1 内存管理概念
内存的基础知识
内存的概念及作用
内存:可存放数据。程序执行前需要先放到内存中才能被CPU处理——缓和CPU与硬盘之间的速度矛盾
地址:内存地址从0开始,每个地址对应一个存储单元
存储单元:内存中也有一个一个的“小房间”,每个小房间就是一个“存储单元”
按字节编址:如果计算机“按字节编址”,则每个存储单元大小为1字节,即1B,即8个二进制位
按字编址:如果字长为16位的计算机“按字编址”,则每个存储单元大小为1个字;每个字的大小为16个二进制位
进程运行的基本原理
指令的工作原理:指令的工作基于“地址”。每个地址对应一个数据的存储单元。操作码+若干参数(可能包含地址参数)
逻辑地址(相对地址):程序经过编译、链接后生成的指令中指明的是逻辑地址(相对地址),即:相对于进程的起始地址而言的地址。 物理地址(绝对地址):变量x的正确存放位置。
从写程序到程序运行:编辑源代码文件;(编译)由源代码文件生成目标模块,即高级语言“翻译”为机器语言;(链接)由目标模块生成装入模块,链接后形成完整的逻辑地址;(装入)将装入模块装入内存,装入后形成物理地址。
三种链接方式:静态链接,装入前链接成一个完整装入模块;装入时动态链接:运行前边装入边链接;运行时动态链接,运行时需要目标模块才装入并链接
三种装入方式
绝对装入:在编译时,如果知道程序将放到内存中的哪个位置,编译程序将产生绝对地址的目标代码。装入程序按照装入模块中的地址,将程序和数据装入内存。绝对装入只适用于单道程序环境。程序中使用的绝对地址,可在编译或汇编时给出,也可由程序员直接赋予。通常情况下都是编译或汇编时再转换为绝对地址。
可重定位装入:静态重定位又称可重定位装入。编译、链接后的装入模块的地址都是从0开始的,指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。可根据内存的当前情况,将装入模块装入到内存的适当位置。装入时对地址进行“重定位”,将逻辑地址变换为物理地址(地址变换是在装入时一次完成的)。特点是在一个作业装入内存时,必须分配其要求的全部内存空间,如果没有足够的内存,就不能装入该作业。作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间。
动态重定位:又称动态运行时装入。编译、链接后的装入模块的地址都是从0开始的。装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。因此装入内存后所有的地址依然是逻辑地址。这种方式需要一个重定位寄存器的支持。
内存管理的概念
操作系统对内存进行管理
负责内存空间的分配与回收
需要提供某种技术从逻辑上对内存空间的扩充(实现虚拟性)
提供地址转换功能,负责程序的逻辑地址与物理地址的转换
提供内存保护功能。保证各进程在各自存储空间内运行,互不干扰
地址转换
为了使编程更方便,程序员写程序时应该只需要关注指令、数据的逻辑地址。而逻辑地址到物理地址的转换(这个过程称为地址重定位)应该由操作系统负责,这样就保证了程序员写程序时不需要关注物理内存的实际情况。
三种装入方式
绝对装入:编译时产生绝对地址(单道程序阶段,此时还没产生操作系统)
可重定位装入:装入时将逻辑地址转换为物理地址(用于早期的多道批处理操作系统)
动态运行时装入(现代操作系统):运行时将逻辑地址转换为物理地址,需设置重定位寄存器
存储保护
保证各进程在自己的内存空间内运行,不会越界访问
两种方式:
方法一:在CPU中设置一对上、下限寄存器,存放进程的上、下限地址。进程的指令要访问某个地址时,CPU检查是否越界。
方法二:采用重定位寄存器(又称基址寄存器)和界地址寄存器(又称限长寄存器)进行越界检查。重定位寄存器中存放的是进程的起始物理地址。界地址寄存器中存放的是进程的最大逻辑地址。
覆盖与交换
覆盖技术
设计思想:将程序分为多个段(多个模块)。常用的段常驻内存,不常用的段在需要时调入内存。
内存中分为一个“固定区”和若干个“覆盖区”。需要常驻内存的段放在“固定区”中,调入后就不再调出(除非运行结束)不常用的段放在“覆盖区”,需要用到时调入内存,用不到时调出内存。
一个“固定区”:存放最活跃的程序段;固定区中的程序段在运行过程中不会调入调出
若干覆盖区:不可能同时被访问程序段可共享一个覆盖区;覆盖区中的程序段在运行过程中会根据需要调入调出
必须由程序员声明覆盖结构,操作系统完成自动覆盖
缺点:对用户不透明,增加了用户编程负担。覆盖技术只用于早期的操作系统中,现在已成为历史。
交换技术
设计思想:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)
中级调度(内存调度):就是要决定将哪个处于挂起状态的进程重新调入内存。
具有对换功能的操作系统中,通常把磁盘空间分为文件区和对换区两部分。
文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;
对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式(学过文件管理章节后即可理解)。总之,对换区的I/O速度比文件区的更快。
注意:PCB会常驻内存,不会被换出外存
覆盖与交换的区别
覆盖是在同一程序或进程中的
交换是在不同进程(或作业)之间的
连续分配管理方式:指为用户进程分配的必须是一个连续的内存空间。
单一连续分配
内存被分为系统区和用户区。系统区通常位于内存的低地址部分,用于存放操作系统相关数据;用户区用于存放用户进程相关数据。
内存中只能有一道用户程序,用户程序独占整个用户区空间。
优点:实现简单;无外部碎片;可以采用覆盖技术扩充内存;不一定需要采取内存保护(eg:早期的PC操作系统MS-DOS)。缺点:只能用于单用户、单任务的操作系统中;有内部碎片(分配给某进程的内存区域中没有用到的部分为内存碎片);存储器利用率极低。
固定分区分配
操作系统需要建立一个数据结构——分区说明表,来实现各个分区的分配与回收。每个表项对应一个分区,通常按分区大小排列。每个表项包括对应分区的大小、起始地址、状态(是否已分配)。
分区大小相等:缺乏灵活性,但是很适合用于用一台计算机控制多个相同对象的场合(比如:钢铁厂有n个相同的炼钢炉,就可把内存分为n个大小相等的区域存放n个炼钢炉控制程序)
分区大小不等:增加了灵活性,可以满足不同大小的进程需求。根据常在系统中运行的作业大小情况进行划分(比如:划分多个小分区、适量中等分区、少量大分区)
优点:实现简单,无外部碎片。缺点:a.当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这又会降低性能;b.会产生内部碎片,内存利用率低。
动态分区分配
动态分区分配又称为可变分区分配。这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。(eg:假设某计算机内存大小为64MB,系统区8MB,用户区共56MB)
动态分区分配没有内部碎片,但是有外部碎片。内部碎片,分配给某进程的内存区域中,如果有些部分没有用上。外部碎片,是指内存中的某些空闲分区由于太小而难以利用。可以通过紧凑(拼凑,Compaction)技术来解决外部碎片。
相邻的空闲分区要合并
动态分区分配算法
在动态分区分配方式中,当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?
首次适应算法(First Fit)
算法思想:每次都从低地址开始查找,找到第一个能满足大小的空闲分区。
如何实现:空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
优点:综合看性能最好。算法开销小,回收分区后一般不需要对空闲分区队列重新排序。
最佳适应算法(Best Fit)
算法思想:由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间,可以尽可能多地留下大片的空闲区,即优先使用更小的空闲区。
如何实现:空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
优点:会有更多的大分区被保留下来,更能满足大进程需求。 缺点:每次都选最小的分区进行分配,会留下越来越多的、很小的、难以利用的内存块。因此这种方法会产生很多的外部碎片。算法开销大,回收分区后可能需要对空闲分区队列重新排序。
最坏适应算法(Worst Fit)
算法思想:又称最大适应算法(LargestFit),为了解决最佳适应算法的问题——即留下太多难以利用的小碎片,可以在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。
如何实现:空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
优点:可以减少难以利用的小碎片。缺点:会较大的连续空闲区被迅速用完。如果之后有“大进程”到达,就没有内存分区可用了。(算法开销大,同上)
邻近适应算法(Next Fit)
算法思想:首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查找结束的位置开始检索,就能解决上述问题。
如何实现:空闲分区以地址递增的顺序排列(可排成一个循环链表)。每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
优点:不用每次都从低地址的小分区开始检索。算法开销小(原因同首次适应算法)。缺点:会使高地址的大分区也被用完。
第三章 内存管理 3.1 内存管理概念
非连续分配管理模式(非连续分配:为用户进程分配的可以是一些分散的内存空间。)
基本分页存储管理
相关概念
地址空间:回顾逻辑地址(相对地址)、物理地址(绝对地址)
页框与页框号:将内存空间分为一个个大小相等的分区(比如:每个分区4KB),每个分区就是一个“页框”(页框=页帧=内存块=物理块=物理页面)。每个页框有一个编号,即“页框号”(页框号=页帧号=内存块号=物理块号=物理页号),页框号从0开始。
页面与页号:将进程的逻辑地址空间也分为与页框大小相等的一个个部分,每个部分称为一个“页”或“页面”。每个页面也有一个编号,即“页号”,页号也是从0开始。
分页存储:操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。
进程的页面与内存的页框有一一对应的关系。各个页面不必连续存放,可以放到不相邻的各个页框中。分页存储有可能产生内部碎片,因此页框不能太大,否则可能产生过大的内部碎片造成浪费。
页表:方便了解进程的每个页面在内存中存放的位置。通常存在PCB(进程控制块)中。
1.一个进程对应一张页表
2.进程的每个页面对应一个页表项
3.每个页表项由“页号”和“块号”组成
4.页表记录进程页面和实际存放的内存块之间的映射关系
5.每个页表项的长度是相同的5.每个页表项的长度是相同的
页表项连续存放,页表中的页号可以是隐含的,即页号不占用存储空间
问题1:每个页表项占多少字节? Eg:假设某系统物理内存大小为4GB,页面大小为4KB,则每个页表项至少应该为多少字节? 内存块大小=页面大小=4KB= 2的12次方B >4GB的内存总共会被分为2的32次方/2的12次方=2的20次方个内存块 >内存块号的范围应该是0~ 2的20次方-1 >内存块号至少要用20bit来表示 >至少要用3B来表示块号(3*8=24bit) 重要重要重要考点:计算机中内存块的数量—>页表项中块号至少占多少字节
问题2:如何实现地址的转换? (基本地址变换机构) 特点:虽然进程的各个页面是离散存放的,但是页面内部是连续存放的 如果要访问逻辑地址A,则①确定逻辑地址A对应的“页号”P;②找到P号页面在内存中的起始地址(需要查页表);③确定逻辑地址A的“页内偏移量”W。 即逻辑地址A对应的物理地址=P号页面在内存中的起始地址+页内偏移量W 访问逻辑地址的访存次数:两次访存
确定逻辑地址对应页号、页内偏移量:页号=逻辑地址/页面长度(取除法的整数部分);页内偏移量=逻辑地址%页面长度(取除法的余数部分)
在计算机内部,地址是用二进制表示的,如果页面大小刚好是2的整数幂,则计算机硬件可以很快速的把逻辑地址拆分成(页号,页内偏移量)。
①逻辑地址的拆分更加迅速——如果每个页面大小为2的K次方B,用二进制数表示逻辑地址,则末尾K位即为页内偏移量,其余部分就是页号。因此,如果让每个页面的大小为2的整数幂,计算机硬件就可以很方便地得出一个逻辑地址对应的页号和页内偏移量,而无需进行除法运算,从而提升了运行速度。
②物理地址的计算更加迅速——根据逻辑地址得到页号,根据页号查询页表从而找到页面存放的内存块号,将二进制表示的内存块号和页内偏移量拼接起来,就可以得到最终的物理地址。
具有快表的地址变换机构
快表:又称联想寄存器(TLB,translation lookaside buffer),是一种访问速度比内存快很多的高速缓存(TLB不是内存!),用来存放最近访问的页表项的副本,可以加速地址变换的速度。与此对应,内存中的页表常称为慢表。
引入快表后地址变换过程:
①CPU给出逻辑地址,由某个硬件算得页号、页内偏移量,将页号与快表中的所有页号进行比较。
②如果找到匹配的页号,说明要访问的页表项在快表中有副本,则直接从中取出该页对应的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表命中,则访问某个逻辑地址仅需一次访存即可。
③如果没有找到匹配的页号,则需要访问内存中的页表,找到对应页表项,得到页面存放的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表未命中,则访问某个逻辑地址需要两次访存(注意:在找到页表项后,应同时将其存入快表,以便后面可能的再次访问。但若快表已满,则必须按照一定的算法对旧的页表项进行替换)由于查询快表的速度比查询页表的速度快很多,因此只要快表命中,只需一次访存。
因为局部性原理,一般来说快表的命中率可以达到90%以上。
局部性原理
时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。(因为很多数据在内存中都是连续存放的)
地址变换过程总结
基本地址变换机构
①算页号、页内偏移量
②检查页号合法性
③查页表,找到页面存放的内存块号
④根据内存块号与页内偏移量得到物理地址
⑤访问目标内存单元
访问逻辑地址的访存次数:两次访存
具有快表的地址变换机构
①算页号、页内偏移量
②检查页号合法性
③查快表。若命中,即可知道页面存放的内存块号,可直接进行⑤;若未命中则进行④
④查页表,找到页面存放的内存块号,并且将页表项复制到快表中
⑤根据内存块号与页内偏移量得到物理地址
⑥访问目标内存单元
访问逻辑地址的访存次数:快表命中,只需一次访存快表未命中,需要两次访存
两级页表
单极页表存在的问题: 1.页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框。 2.没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面。
解决方法:把页表再分页并离散存储,然后再建立一张页表记录页表各个部分的存放位置,称为页目录表,或称外层页表,或称顶层页表
两级页表
长长的页表再分页
逻辑地址结构:一级页号,二级页号,页内偏移量(根据逻辑地址位数、页面大小、页表项大小确定多级页表的逻辑地址结构)
实现地址变换
①按照地址结构将逻辑地址拆分成三部分 ②从PCB中读出页目录表始址,再根据一级页号查页目录表,找到下一级页表在内存中的存放位置 ③根据二级页号查二级页表,找到最终想访问的内存块号 ④结合页内偏移量得到物理地址
注意:1.多级页表中,各级页表的大小不能超过一个页面。若两级页表不够,可以分更多级;2.多级页表的访存次数(假设没有快表机构)——N级页表访问一个逻辑地址需要N+1次访存。
基本分段存储管理
进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址。由于是按逻辑功能模块划分,用户编程更方便,程序的可读性更高
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
分段:分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。
段号的位数决定了每个进程最多可以分几个段
段内地址位数决定了每个段的最大长度是多少
段表
程序分多个段,各段离散地装入内存,为了保证程序能正常运行,就必须能从物理内存中找到各个逻辑段的存放位置。为此,需为每个进程建立一张段映射表,简称“段表”。
1.每个段对应一个段表项,其中记录了该段在内存中的起始位置(又称“基址”)和段的长度。
2.各个段表项的长度是相同的。段号可以是隐含的,不占存储空间。
地址变换
①根据逻辑地址得到段号、段内地址; ②判断是否越界:段号与段表寄存器中的段长度比较; ③由段表始址、段号找到对应的段表项; ④根据段表中记录的段长,检查段内地址是否越界; ⑤计算得到物理地址:段基址+段内地址; ⑥访问目标内存单元。
分段、分页管理的对比
页是信息的物理单位。对用户是不可见的。 段是信息的逻辑单位。分段对用户是可见的,用户编程时需要显式地给出段名。
页的大小固定且由系统决定。 段的长度却不固定,决定于用户编写的程序。
分页的用户进程地址空间是一维的,程序员只需给出一个记忆符即可表示一个地址。 分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段内地址。
分段比分页更容易实现信息的共享和保护。不能被修改的代码称为纯代码或可重入代码(不属于临界资源),这样的代码是可以共享的。可修改的代码是不能共享的
分页(单级页表):第一次访存——查内存中的页表,第二次访存——访问目标内存单元。总共两次访存。 分段:第一次访存——查内存中的段表,第二次访存——访问目标内存单元。总共两次访存与分页系统类似,分段系统中也可以引入快表机构,将近期访问过的段表项放到快表中,这样可以少一次访问,加快地址变换速度。
驻留性:作业数据在整个运行期间都会常驻内存
分段+分页=段页式管理:将进程按逻辑模块分段,再将各段分页(如每个页面4KB)再将内存空间分为大小相同的内存块/页框/页帧/物理块进程前将各页面分别装入各内存块中
逻辑结构:段页式系统的逻辑地址结构由段号、页号、页内地址(页内偏移量)组成。
段号的位数决定了每个进程最多可以分几个段
页号位数决定了每个段最大有多少页
页内偏移量决定了页面大小、内存块大小是多少
地址结构:二维
段表、页表
每个段对应一个段表项,每个段表项由段号、页表长度、页表存放块号(页表起始地址)组成。每个段表项长度相等,段号是隐含的。
每个页面对应一个页表项,每个页表项由页号、页面存放的内存块号组成。每个页表项长度相等,页号是隐含的。
地址变换
①根据逻辑地址得到段号、页号、页内偏移量; ②判断是否越界:段号与段表寄存器中的段长度比较; ③由段表始址、段号找到对应的段表项; ④根据段表中记录的页表长度,检查页号是否越界; ⑤由段表中的页表地址、页号得到查询页表,找到相应页表项; ⑥由页面存放的内存块号、页内偏移量得到最终物理地址; ⑦访问目标单元。
访问一个逻辑地址所需访存次数
第一次:查段表;第二次:查页表;第三次:访问目标单元。
可引入快表机构,以段号和页号为关键字查询快表,即可直接找到最终的目标页面存放位置。引入快表后仅需一次访存。