博客主页:🏆看看是李XX还是李歘歘 🏆
🌺每天不定期分享一些包括但不限于计算机基础、算法、后端开发相关的知识点,以及职场小菜鸡的生活。🌺
💗点关注不迷路,总有一些📖知识点📖是你想要的💗
⛽️今天的内容是7000字的内存管理相关的知识点,篇幅较长,建议先收藏后阅读⛽️💻💻💻
操作系统中的进程管理请参考:操作系统之进程问题总结——进来背书_李歘歘的博客-CSDN博客
目录
内存管理
内存管理的功能
- 内存空间的分配与回收:由操作系统完成主存储器空间的分配和管理,使程序员摆脱存储分配的麻烦,提高编程效率。
- 地址转换:在多道程序环境下,程序中的逻辑地址与内存中的物理地址不可能一致,因此存储管理必须提供地址变换功能,把逻辑地址转换成相应的物理地址。
- 内存空间的扩充:利用虚拟存储技术或自动覆盖技术,从逻辑上扩充内存。
- 存储保护:保证各道作业在各自的存储空间内运行,互不干扰。
程序装入和链接
创建进程首先要将程序和数据装入内存。将用户源程序变为可在内存中执行的程序,通常需要以下几个步骤:
- 编译:由编译程序将用户源代码编译成若干个目标模块。
- 链接:由链接程序将编译后形成的一组目标模块,以及所需库函数链接在一起,形成一个完整的装入模块。
- 装入:由装入程序将装入模块装入内存运行。
内存保护
采取两种方法
- 在CPU中设置一对上、下限寄存器,存放用户作业在主存中的上下限地址,每当CPU要访问一个地址时,分别和两个寄存器的值相比,判断有无越界。
- 采用重定位寄存器(或基址寄存器)和界地址寄存器(限长寄存器)来实现这种保护。
重定位寄存器含最小的物理地址值,界地址寄存器含逻辑地址值。每个逻辑地址值必须小于界地址寄存器;内存管理机构动态地将逻辑地址与界地址寄存器进行比较,如果未发生地址越界,则加上重定位寄存器的值后映射成物理地址,再送交内存单元。
内存覆盖与内存交换
覆盖:把用户空间分成一个固定区和若干个覆盖区。将经常活跃的部分放在固定区,其余部分按调用关系分段。首先将那些即将要访问的段放入覆盖区,其他段放在外存中,在需要调用前,系统再将其调入覆盖区,替换覆盖区中原有的段。
交换(对换):把处于等待状态(或在CPU调度原则下被剥夺运行权利)的程序从内存移到辅存,把内存空间腾出来,这一过程又叫换出;把准备好竞争CPU运行的程序从辅存移到内存,这一过程又称为换入。中级调度就是釆用交换技术。
交换技术主要是在不同进程(或作业)之间进行,而覆盖则用于同一个程序或进程中。
覆盖技术要求给出程序段之间的覆盖结构,使得其对用户和程序员不透明,所以对于主存无法存放用户程序的矛盾,现代操作系统是通过虚拟内存技术来解决的,覆盖技术则已成为历史。
内存连续分配管理方式
为一个用户程序分配一个连续的内存空间,包括单一连续分配、固定分区分配和动态分区分配。
分配方法 | 说明 | 优点 | 缺点 |
单一连续分配 | 分为系统区和用户区,系统区仅提供给操作系统使用,通常在低地址部分;用户区是为用户提供的、除系统区之外的内存空间。这种方式无需进行内存保护。 |
| 只能用于单用户、单任务的操作系统中,有内部碎片,存储器的利用率极低。 |
固定分区分配 | 将用户内存空间划分为若干个固定大小的区域,每个分区只装入一道作业; 固定分区分配在划分分区时,有两种不同的方法:
通常将分区按大小排队,并为之建立一张分区说明表,分配时检索该表,以找到合适的分区给予分配,将其状态置为”已分配” | 可用于多道程序设计最简单的存储分配,无外部碎片。 |
|
动态分区分配 | 又称为可变分区分配,是一种动态划分内存的分区方法。这种分区方法不预先将内存划分,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。 |
| 内存中会产生越来越多的外部碎片【可用拼接技术为已解决外部碎片】 |
内部碎片:分配给某些进程的内存区域中,如果有些部分没有用上,(动态分配,按需分配,对于进程来说,没有空的)。
外部碎片:是指内存中的一些空闲分区由于太小而难以利用。
动态分区分配策略
- 首次适应(First Fit)算法:空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。
- 最佳适应(Best Fit)算法:空闲分区按容量递增形成分区链,找到第一个能满足要求的空闲分区。
- 最坏适应(Worst Fit)算法:又称最大适应(Largest Fit)算法,空闲分区以容量递减的次序链接。找到第一个能满足要求的空闲分区,也就是挑选出最大的分区。
- 邻近适应(Next Fit)算法:又称循环首次适应算法,由首次适应算法演变而成。不同之处是分配内存时从上次查找结束的位置开始继续查找。
首次适应算法不仅是最简单的,而且通常也是最好和最快的。但是它会使得内存的低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。
邻近适应算法试图解决这个问题,但实际上,它常常会导致在内存的末尾分配空间(因为在一遍扫描中,内存前面部分使用后再释放时,不会参与分配),会使高地址的大分区也被用完,分裂成小碎片。它通常比首次适应算法的结果要差。
最佳适应的分配会留下很小的难以利用的内存块,它会产生最多的外部碎片。
最坏适应算法与最佳适应算法相反,选择最大的可用块,把最大的连续内存划分开,会很快导致没有可用的大的内存块。
内存非连续分配管理方式
非连续分配允许一个程序分散地装入到不相邻的内存分区中,根据分区的大小是否固定分为基本分页存储管理方式、基本分段存储管理方式和段页式存储管理方式。
1.基本分页存储管理方式
设计思路:将主存空间划分为大小相等且固定的块(页),作为主存的基本单位,进程以块为单位申请空间,相对于固定分区技术,分页相对于分区很小,分页管理不会产生外部碎片,产生的內部碎片也非常的小。
页表:为了便于在内存中找到进程的毎个页面对应的物理块,系统为每个进程建立一张页表,记录页面在内存中对应的物理块号,页表一般放在内存中。
逻辑地址结构的表示方法:页号(有多少页的编号)+页内偏移(页内存了多少东西)。
物理地址结构的表示方法:块号(有多少块的编号)+页内偏移(页内存了多少东西)。
物理地址:页表中页号对应的物理内存块号+地址结构中的页内偏移
地址变换的计算过程:
- 页号P=A/L,页内偏移量W=A%L;
- 比较页号P和页表长度M,若P>=M产生越界中断;
- 块号b=页表始址F+页号P*页表项长度
- 物理地址E=b*L+W
- 拿E即可访问内存地址
补充说明:
L是页面大小/物理块的大小,L太小:进程页面数过多,页表数量多,增加内存占用,降低硬件地址转换效率;L太大页内碎片过多,降低内存利用率。
页表项:页号与其对应的物理块号称之为一个页表项,页表项大小的设计应当尽量一页正好能装下所有的页表项。
系统中只设置一个页表寄存器PTR,存放页表在内存中的起始地址F和页表长度M。平时,进程没有执行时,页表的起始地址和页表长度存放在进程的PCB中,当调度到进程时,才将这两个数据装入到页表寄存器中。
快表:
如果页表放在内存中,按照上面的方法取一条数据要访问两次内存:第一次是访问页表,确定所存取的数据或指令的物理地址;第二次是根据该地址存取数据或指令。
为了解决访问两次内存的问题,可以在地址变换机构中増加一个具有并行査找能力的髙速缓冲寄存器(快表),又称为相联存储器(TLB),用来存放当前访问的若干页表项,以加速地址变换的过程。相联存储器既可以按照地址査找也可以按照內容査找。
在具有快表的分页机制中:地址的变换过程如下
①CPU给出逻辑地址后,由硬件进行地址转换,将页号送入高速缓存寄器,并将此页号与快表屮的所有页号进行比较。
②若找到匹配的页号,说明所要访问的页表项在快表中,则直接从中取出该页对应的页号,与页内偏移量拼接形成物理地址。
③若未找到匹配的页号,则需要访问内存中的页表,在读出页表项后,应同时将其存入快表,以便后面可能的再次访间。但若快表己满,则必须按照一定的算法对旧的页表项进行替换。
注意:有些处理机设计为快表和慢表同时查找,若在快表中查找成功,则终止慢表的查找。快表的有效性基于著名的局部性原理。
二级页表:
如果页数过多,就会导致页表也过多,可以考虑设置一个用来储存页表的页表,逻辑地址空间格式=一级页号+二级页号+页内偏移,但是一定要保证顶级页表只有一个
2.基本分段存储管理方式
分页和分段的区别:
分页是从计算机角度考虑设计的,目的是为了内存的利用率,提高计算机性能,分页通过硬件机制实现,对用户完全透明。
分段是从用户和程序员的角度提出,满足方便编程,信息保护和共享,动态增长及动态链接等多方面的需要。
设计思路:分段是按照用户进程中的自然段划分逻辑空间的,地址结构=段号S+段内偏移量W,页式系统中,页号和页内偏移对用户透明,段式系统中段号和段内偏移量必须由用户显示的提供。
逻辑地址结构的表示方法:段号+段内偏移。
地址变换的计算过程:
- 从逻辑地址A中取出前几位为段号S,后几位为段内偏移量W,
- 比较段号S和段表长度M,若S≥M,则产生越界中断,否则继续执行。
- 段表中段号S对应的段表项地址=段表始址F+段号S*段表项长度,取出该段表项的前几位得到段长C。若段内偏移量W≥C,则产生越界中断,否则继续执行。段表项实际上只有两部分,前几位是段长,后几位是始址。
- 取出段表项中该段的始址b,计算E=b+W
- 用得到的物理地址E去访问内存。
段的保护机制:存取控制保护;地址越界保护。
与页式管理不同,段式管理不能通过给出一个整数便确定对应的物理地址,因为每段的长度是不固定的,无法通过整数除法得出段号,也无法通过求余得出段内偏移,所以段号和段内偏移定要显式给出(段号,段内偏移),因此分段管理的地址空间是二维的。
3.段页式存储管理方式
页式存储有效的提高内存利用率,分段存储能反映程序的逻辑结构并有利于段的分享,将这两种方式结合可以得到段页式存储管理方式
作业的地址空间首先被分成若干逻辑段,每段有自己的段号,每个段分成若干大小固定的页,对内存空间的管理仍然和分页存储管理一样。
逻辑地址结构:段号S+页号P+页内偏移量W
地址变换的计算过程:
- 从逻辑地址A中取出前几位为段号S,中间几位为页号P,后几位为页内位移W。
- 比较段号S和段表长度M,若S>M,则产生越界中断。
- 取出段表起始地址F与段号S相加,用得到的地址值到内存中取出该内存单元存放的数。取出来的数的前几位是页表长度C,后几位是页表起始地址d,若页号P>C,则产生越界中断。
- 页表起始地址d与页号P和页表项长度的乘积相加得到页表项在内存中的物理地址,查找到该地址存放的数值为物理块号b
- 用物理块号b与页内位移W组合成物理地址E。
- 用得到的物理地址E去访问内存。
段页式存储管理系统的地址变换需要访问内存3次所以同样可以用高速缓冲寄存器(快表)来减少对内存的访问次数。
注意:在一个迸程中,段表只有一个,而页表可能有多个 。
分页与分段的区别:
分页 | 分段 |
信息的物理单位 | 信息的逻辑单位 |
目的是系统管理所需,为了提高内存利用率 | 目的是为了更好地满足用户的需要 |
页的大小固定且由系统决定 | 段的长度不定,不同的段有不同的段长,是由用户编写的程序决定的 |
作业地址空间是一维的 | 作业地址空间是二维的 |
有内部碎片,无外部碎片 | 无内部碎片,有外部碎片 |
虚拟内存管理
传统存储管理的特征
在说虚拟内存之前,先来看一下传统存储管理,之前所说的内容全部是传统存储管理,具有两个特征:
1.一次性:作业必须一次性全部装入内存后,才能开始运行;
一次性存在两个问题,
- 作业很大无法装入则无法运次;
- 当大量作业要求运行时,由于内存不足,只能一部分作业先运行,导致多道程序度下降。
2.驻留性:作业装入内存后,一直驻留在内存中,任何部分不会被换出,直至作业运行结束。运行中的进程会因等待IO而被阻塞,可能处丁长期等待状态。
局部性原理
局部性原理表现在以下两个方面
时间局部性。程序中的某条指令一旦执行,不久后该指令可能再次执行;某数据被访问过,不久后该数据可能再次被访问。产生时问局部性的典型原因是程序中存在着大量的循环操作。通过将近来使用的指令和数据保存到高速缓冲存储器中,并使用高速缓存的层次结构实现
空间局部性。一过程序访问了某个存储单元,在不久后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。通常使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。
虚拟内存技术实际上建立了“内存-外存”的两级存储器结构,利用局部性原理实现尚速缓存。
虚拟存储器的定义和特征
定义:基于局部性原理,程序的一部分装入内存,一部分留在外存,需要的时候将外存内容调入内存,就好像产生了一个巨大的内存空间。
特征:
- 多次性:作业在运行时,分多次调入内存运行;
- 对换性:作业不必一直驻留内存,允许作业在运行过程中进行换进换出;
- 虚拟性:从逻辑上扩充內存容量,使用户看到的内存容量远大于实际的内存容量。
虚拟存储器的实现
实现方式:
虚拟内存技术允许将一个作业分多次调入内存。采用连续分配方式时,会使相当一部分内存空间都处于暂时或“永久”的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实现需要建立非连续内存管理方式的基础上,所以虚拟内存的实现有请求分页存储管理、请求分段存储管理、请求段页式存储管理三种方式。下面只解释请求分页存储管理。
硬件支持:一定容量的内存和外存、页表机制(或者段表机制)、中断机构、地址变换机构。
请求分页存储管理
实现:建立在基本分页系统基础之上,为了支持虛拟存储器功能而増加了请求调页功能和页面置换功能。请求分页=基本分页+请求调页功能+页面置换功能
页表机制
页表项中增加的4个字段
- 状态位P。用于指示该页是否已调入内存,供程序访问时参考。
- 访问字段A。用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,供置换算法换出页面吋参考。
- 修改位M。标识该页在调入内存后是否被修改过。
- 外存地址。用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考。
缺页中断机构
当访问页面不在内存时,就会产生缺页中断,
特点:指令执行期间产生中断,而不是指令执行之后产生中断和处理中断;一条指令在执行期间,可能产生多次缺页中断。
地址变换机构
请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存又增加了某些功能而形成的。
地址变换流程:
- 检索快表,找到访问页,修改页表项中的访问位,利用页表项中给出的物理块号和页内地址形成物理地址。
- 没有找到该页的页表项,去内存中寻找页表,看该页是否已经调入內存,没有调入则产生缺页中断,请求从外存把该页调入内存。
页面置换算法
最佳置换算法(OPT)
选择永不使用或者最长时间内不再访问的页面进行淘汰,但是现实中是无法预知的
优点:缺页率最小,性能最好
先进先出页面置换算法(FIFO)
优先淘汰最早进入的页面
优点:实现简单
缺点:与进程的实际运行规律不匹配
Belady异常:增大分配的物理块数,但是故障数不减反增【只有先进先出算法会出现】
最近最久未使用置换算法(LRU)
选择最近最长时间没有被访问的页面进行淘汰,毎个页面设置一个访问字段,用来标识上次被访问到现在经历的时间
优点:性能好
缺点:实现复杂需要寄存器和栈的硬件支持,属于堆栈类算法
时钟置换算法(CLOCK)
像一个时钟一样转圈,每个页面设置一个使用位(访问位),遇到没有被使用的就会将页面换出,然后将使用位置0,如果遇到使用的就会将使用位置零,然后扫描下一个
优点:性能接近于最佳置换算法
缺点:实现复杂开销大
改进型CLOCK算法
使用位(访问位)的基础上增加修改位
扫描过程:
扫描缓冲区,选择第一个使用位和修改位都为0的页面换出;
第一步失败后,查找使用位为0,修改位为1的进行替换,对于每个跳过的帧,将使用位置为0
第二步失败后,指针回到初始地点且使用位(访问位)均为0,重复第一步
优点:相对于未改进型,节省了时间
抖动:刚换出的页面又要换入内存,产生的原因是分配的物理页帧数不足(主要原因)和置换算法不当。