1)为什么要进行内存管理?
2)多级页表解决了什么问题?又会带来说明新问题?
内存管理:操作系统对内存的划分和动态分配。
因为操作系统不可能将所有用户进程和系统所需要的全部程序都放入主存,必须要合理有效的规划内存空间问题。
内存管理的主要功能有:
1.内存空间的分配与回收:由操作系统负责内存空间的分配和管理,并且回收结束进程所占有的空间。
2.地址转换:存储管理必须提供地址变换功能,将逻辑地址转换成对应的物理地址。
3.内存空间的扩充:利用虚拟存储技术从逻辑上扩充内存。
4.内存共享:多个合作进程可能需要访问同一块数据,因此必须支持对内存共享区域进行受控访问。
5.存储保护:保证各个进程在各自的存储空间内运行,互不干扰。
一、内存管理的基本原理和要求
1.程序的链接与装入
1)编译:由编译程序将用户源代码编译成若干目标模块。(.c文件)
2)链接:由链接程序将编译后形成的一组目标模块,以及所需要的库函数来链接在一起形成一个完整的装入模块。(.o文件)
3)装入:由装入程序装入模块装入内存运行。(.exe文件)
装入:
1)绝对装入:只适用于单道程序环境,逻辑地址和实际内存地址完全相同。
2)可重定位装入:经过编译和链接后的装入模块的起始地址通常都是从0开始,程序中使用的指令和数据地址都是相对于起始地址而言的,此时就应采用可重定位装入方式。但根据内存的实际情况,在装入时对目标程序中的相对地址的修改过程称为重定位,又因为地址转换通常时在进程装入时一次完成的,所以称为静态重定位。
当一个作业装入内存时,是一次性分配它所要求的全部内存,若无法满足,就不能装入,一旦装入内存,就不能在内存中移动,也不能在另外申请内存空间。
3)动态运行时装入:称为动态重定位。程序装入内存可以在内存中移动,装入内存的所有地址都是相对地址,将地址转换推迟到程序要真正执行时在进行,这就需要一个重定位寄存器来存放装入模块的起始地址。
优点:可将程序分配到不连续的存储区;在程序运行前只需要装入部分代码就可以投入运行,然后再程序运行期间,根据需要再动态申请分配内存空间;便于程序段的共享。
根据链接的时间不同,分为三种链接:
1)静态链接:再程序运行前,先将各个目标模块及他们所需要的库函数链接成一个完整的装入模块,以后不再拆开。
将目标模块装配成一个装入模块时:1.修改相对地址,编译后所有目标模块都是从0开始的相对地址,当链接成一个装入模块时要修改相对地址。2.变换外部调用符号,将每个模块中所用的外部调用符号也都变换为相对地址。
2)装入时动态链接:边装入边链接,优点是便于修改和更新,便于实现对目标模块的共享。
3)运行时动态链接:在程序执行中需要某模块时才去链接,优点是能加快程序装入过程,还可以节省内存空间。
2.逻辑地址与物理地址
在虚拟内存中,形成逻辑地址阶段就是链接阶段。
编译后每个目标模块都是从0号单元开始编址的,这称为该目标模块的相对地址(逻辑地址)。当链接程序将各个模块链接成为一个完整的可执行目标程序时,链接程序就会按顺序将模块的相对地址构成统一的0号单元开始编制的逻辑地址空间(虚拟地址空间)。对于32位系统,逻辑地址空间的范围是0~2^32 - 1。
当程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换为物理地址,称地址重定位。
3.进程的内存映像
当一个程序调入内存运行时就构成了进程的内存映像。
1)代码段:程序的二进制代码,代码段是只读的,可以被多个进程共享。
2)数据段:程序运行时加工处理的对象,包括全局变量和静态变量。
3)进程控制块(PCB):存放在系统区。操作系统通过PCB来控制和管理进程。
4)堆:存放动态分配的变量。malloc动态的向高地址分配空间。
5)栈:用来实现函数调用。从用户空间的最大地址往低地址方向增长。
4.内存保护
多进程在主存中彼此互不干扰的环境下运行,操作系统是通过内存保护来实现的。
1)CPU中设置了一堆上、下限寄存器存放进程在主存中的上限和下限地址,每当CPU要访问一个地址时就分别和这两个寄存器比较,判断是否越界。
2)采用重定位寄存器(基地址寄存器)存放进程的起始物理地址,界地址寄存器(限长寄存器)存放进程的最大逻辑地址,进行越界检查。
内存管理部件将逻辑地址与界地址寄存器进行比较,若没有发生地址越界,就叫加上重定位寄存器的值后映射成物理地址在送到内存单元。
只有内核才能运行这两个存储器,所以必须要使用特权指令。
5.内存共享
可重入代码:也叫纯代码。是一种运行多个进程同时访问但不允许被任何进程修改的代码。
二、连续分配管理方式
1.单一连续分配
内存被分为系统区(仅供操作系统使用,通常在低地址部分)和用户区(一道用户程序独占整个用户区)
2.固定分区分配
是最简单的一种多道程序存储管理方式。将用户内存空间划分为若干固定大小的分区,每个分区只装入一道作业。
1)分区大小相等:程序太小会浪费,程序太大又无法装入,缺乏灵活性。
2)分区大小不等:划分为多个较小的分区、适量的中等分区和少量大分区。
内部碎片的产生:决定内存分配算法仅能把预定大小的内存块分配给客户。
外部碎片的产生: 频繁的分配与回收物理页面会导致大量的、连续且小的页面块夹杂在已分配的页面中间,就会产生外部碎片。
3.动态分区分配
在进程装入内存时动态的分配内存,使分区的大小刚好适合进程的需要,因此系统中分区的大小和数量是可变的。
动态分区分配的内存回收方法:就是把结束掉的进程上下如果有相邻的空闲内存一起合并为一个新的空闲内存。
各种动态分区分配算法的比较:
1)首次适应算法:空闲分区按地址递增的次序排序,每次都从低地址开始往后查找遇到第一个满足大小的空闲分区就开始分配。但这会使内存低地址部分出现很多小碎片,每次分配查找都要经这些分区,加大开销。
2)邻近适应算法:在首次适应算法基础上,并不是每次都从头开始查找,而是每次都接着上一次查找完后的地址开始继续往后查找,相当于一个循环队列,这样让内存低、高地址部分的空闲区就可以以同等概率被分配,划分为小分区,导致内存高地址部分没有大空闲分区可以使用。通常比首次适应算法更差。
3)最佳适应算法:空闲分区按容量递增次序排序。每次分配内存时,顺序查找第一个能满足大小的空闲分区碎片,即最小空闲分区。还要每次对空闲分区的大小进行排序,加大开销,但每次都会留下越来越多很小的难以利用的内存块,进而产生最多的外部碎片。
4)最坏适应算法:空闲分区按容量递减的次序排列。每次分配空闲分区,顺序查找到第一个能满足要求的空闲分区,即最大空闲分区,还要每次对空闲分区的大小进行排序,加大开销,但这样会导致没有大空闲分区,因此性能也很差。
综合来看,首次适应算法开销最小,性能好,回收分区也不需要对空闲分区重新分配。
三、基本分页存储管理
所有进程的页表大多数都驻留在内存中,在系统中只设置一个页表寄存器(PTR)存放在页表的起始地址和长度。只有在调度某进程时,才将这两个数据装入页表寄存器中。每个进程都有一个独立的逻辑地址,有一张属于自己的页表。
固定分区会产生内部碎片,动态分区会产生外部碎片,对内存的利用率都很低,那么就引入了分页思想:将内存分为若干固定大小(如4KB)的分区,称为页框或物理块。进程的逻辑地址空间分为大小相等的若干区域称为页或页面。操作系统以页框为单位为个进程分配内存空间。
也就是说 页面大小==页框大小 页面是进程分成的若干区域,页框是内存分成的若干区域,页面跟页框是一一对应的,页号和页框号也是一一对应的。
1.分页存储的概念
分页管理不产生外部碎片,只会在最后一个不完整的块上产生内部碎片
2.基本地址变换机构
整个地址变换过程均是由硬件自动完成的。
在进程未执行时,页表的起始地址和页表长度存放在本进程的PCB里,当进程被执行时才将它放入寄存器中。设页面大小L,逻辑地址A,物理地址E
计算步骤
确定页表项大小
1)每次访存操作都需要进行逻辑地址到物理地址的转换,地址转换过程必须足够快,不然会降低访存速度。
2)每个进程引入页表,用于存储映射机制,页表不能过大,否则内存利用率会降低。
3.具有快表的地址变换机构
地址变换机构用于将指令中的逻辑地址与重定位寄存器中的基地址相加得到物理地址。
高速缓存存储器——快表,主存中的页表称为慢表。
具有快表的分页机制中,地址的变换过程:
快表效率提升证明:
局部性原理:
4.两级页表
1)对于页表所需要的内存空间,采用离散分配方式,用一张索引表来记录各个页表的存放位置,这就解决了页表占用连续存储空间的问题;
2)只需要将当前需要的部分页表调入内存,其余页表仍放在磁盘,需要的时候在进行调入(虚拟内存思想),这就解决了页表占用内存过多的问题。
两级页表:
两个小细节:
n级页表访问内存的次数就是n+1次
二级页表中的地址变换过程:
四、基本分段存储管理
1.分段
分段是在用户编程的时候,将程序按照逻辑划分为几个逻辑段。
在分段系统中,段号和段内偏移量必须由用户显示提供,这个工作由编译程序完成。
分段系统将用户进程的逻辑地址空间划分为大小不等的段。
2.段表
每个进程都有一张逻辑空间与内存空间映射的段表。进程的每一个段都对应一个段表项。
段表项记录了该段在内存中的起始地址和段的长度。
一个进程对应一张段表,n个进程n个段表。一个进程,n个段也只需要一个段表。
3.地址变换机构
4.分页和分段的对比
页表和段表在同样的内存中,系统提供给用户的物理地址空间为在空间大小减去页表或段表的长度。由于页表和段表的长度不能确定,所以提供给用户的物理地址空间大小也不能确定。
5.段的共享与保护
若有些段可以被多个进程共享,则可以用一个单独的共享段表来描述这些段,不需要再每个进程的段表中都保存一份。
共享段表的作用:实现多个进程共享同一段代码或数据,这样既能节省空间,又能实现对数据段的更新和维护。
多个进程空间共享同一段逻辑空间是不可能的,因为每个进程的逻辑地址空间都是独立的,但他们都指向同一个物理地址。
在分页系统中,虽然也能实现共享,但远不如分段系统来的方便。若被共享的代码占N个页框,则每个进程的页表中都要建立N个页表项,指向被共享的N个页框。
在分段系统中,不管该段有多大,都只需要设置一个段表项,因此非常容易实现共享。只需要在每个进程中设置一个段表项,只向被共享的一个物理段。
不能被任何进程修改的代码叫可重入代码或纯代码,在每个进程中都必须配以局部数据区,将在执行的过程中可能改变的部分复制到数据区,这样就可对数据区的内容进行修改。
五、段页式存储管理
每个进程有一张段表,每个段有一张页表。
在段页式管理中,进程的段表只有一个,而页表可能有多个。
进程的地址空间首先被分成若干逻辑段,每段都有自己的段号,然后将每段分成若干固定大小的页。对内存空间的管理仍然和分页存储管理一样,将其分成若干和页面大小相同的存储块,对内存的分配以存储块为单位。
段页式管理的地址空间是二维的。
1)为什么要进行内存管理?
引入多道处理系统后,如果不进行内存管理,会造成数据混乱,为了刚好支持多道程序并发执行。
2)多级页表解决了什么问题?又会带来说明新问题?
解决了逻辑地址空间过大,页表长度会大大增加的问题。