内存管理学啥:
1、内存保护:主要是每个进程之间只去访问自己有效的那块区域
2、地址转换:如何定位我想要的代码在哪个地方的那块
3、内存的分配和回收:
- 连续存储
- 不连续存储(段页式)
4、内存空间的扩充(基于时间/空间局限性:)一整个程序不一定要全部进入内存空间内,需要用时在去外存找TA即可
思维导图:
第一part:内存管理的相关概念
什么是内存?
复习知识点,芬诺伊曼体系架构:控制器、计算器、内存、输入设备、输出设备
芬诺伊曼体系以控制器和计算器(cpu为核心)
随着计算机的发展芬诺伊曼体系依然被沿用,但以cpu为核心渐渐演变为以内存为核心。
注意这里的核心不是说ta最重要,从学的感觉来说就是,cpu更多像是流水线工人,TA去执行一些指令和运算。内存更像是一个仓库,创建进程先到TA这里.然后运行是备好东西“送”到cpu那里,最后算好先放内存那里,怎么办在做决定(放回外存、输出到其他地方都可以)
复习结束。
内存:存数据的。 怕进程读取错代码所以需要给TA们一个编号(地址)
ps:内存读写速度大于外存
补充知识:只要是2的x次方 x%10=0&&x/10=i(一个数字) 每一个数字都有一个单位,如:10对应k 20对应g……
指令(机器语言):通过0、1的不同组合告诉你一步一步应该做什么
一点小感悟:计算机其实也是一个小笨蛋,他需要我们一步一步教,大家之所以会在写代码的时候觉得他很厉害是因为有人替我们把这些指令拼在了一起,我们就能很简单的去写代码啦。伟大的是能把他们封装起来的程序员。
代码被编写为文件 之后需要编译为机器语言 然后拼装成模块放入内存中
三种模式(举例子):
静态模式:网购时,买的东西到手(内存时)就是一个完整的
转入时动态链接:网购时买的东西都齐了,但你得自己拼
运行时动态链接:你有一定的钱在steam里,你只有这么多,但你又想玩很多,你可以买一款玩完然后退了再买下一款
装入方式:
绝对装入:在执行前把代码固定死,之后也无法修改
静态重定位:全部进程需要进入内存,在申请时对地址的偏移进行记录,之后无法改变
动态重定位:
地址转换在什么时候
答:代码执行时
需要重定位寄存器支持 ps:程序可以在内存中移动。
内存保护:
- 设置上下限寄存器
- 利用重定位寄存器、界地址寄存器判断
内存管理管什么?
操作系统负责对进程进行内存空间的分配与回收。
操作系统需要提供某种技术从逻辑上对内存空间进行扩充。(虚拟存储技术(操作系统的虚拟性)、覆盖技术、交换技术)
操作系统需要提供地址转换功能,负责程序的逻辑地址与物理地址的转换。(三种装入方式)
操作系统需要提供内存保护功能,保证各进程在各自存储空间内运互不干扰。
ps这一段在开篇就提到过
承前启后part:
这一part是我在真正学习时遇到的问题,王道的将很多知识点冗杂在一起这一part是在告诉我们,前面我们学习了内存保护,以及装入方式,装入方式中绝对装入、静态重定位会被应用在内存与分配中连续分配的技术中,运行时动态链接会被应用在非连续分配以及虚拟化中
而内存的分配和回收其实可以理解为虚拟化的工具,就好比程序员和编程工具之间的关系,但也基于内存的分配和回收进行了一定的优化
第二part:内存的分配和回收:
连续分配:
单一连续分配:单道程序
除了系统运行所需要的内存,其他内存内只允许进入一个进程
优点:实现简单,无外部碎片
缺点:只支持一个用户程序 cpu利用率低,内部碎片低。
固定分区分配:应用于多道程序
特点:在被生产出来时他被分为多个固定的区域(你被规定是多少内存你就不会改变)
分配方式:
分区大小相同
分区大小不相同
ps:各有特点
无外部碎片有内部碎片
动态分区分配
动态分配的思想:
特点:初始时内存大小是不被划分和固定的,在程序申请内存时根据程序大小进行划分
怎么去理解呢?
答:假设计算机内存是16g,程序要多少(不超过我剩余的就行),我就给多少,
ps:无内部碎片有外部碎片
动态分配的策略/算法:
首次适应算法
算法:从低地址出发,遇到合适的无论大小只要满足,就将资源装入其中。
优点:算法实现容易
缺点:因为是首次适应,地址相比较后面的使用少,且不考虑分配完的剩余空间,可能存在外部碎片
最佳适应算法:
算法思想:将内存按内存空间的大小由小到大进行排序,再由算法去寻找第一个大于或者等于的空间进行分配。
当然算法思想不唯一,可以是:合并和减少了资源需要需要重新排序
优点:合理分配好资源。
缺点:会产生外部碎片。比如:你要4kb的存储空间。刚好有个5kb的,他分配完后只剩1kb,很难分配
最坏适应算法:
算法思想:将内存按内存空间的大小由大到小进行排序,每次去寻找最大的空间装入
优点:不易产生外部碎片。
缺点:他先分配的是最大的部分。若本身剩的空间很大,却因为这个算法,被分割成很多很小的空间。就无法写入本身能写入的代码。
临近适应算法:
我更夏欢叫他循环首次适应算法。
他是首次适应算法的变形。它的出发点不再是低地址。而是上次存入的地址末尾然后往下寻找。做一个循环。
优点:相较来说这个算法每个空间的利用率得到了提高。
总结:
非连续分配:
基本分页式管理:
将整个空间划分为很多个大小相同页。由页号*固定的大小+页内偏移量(这里我们可以大致看出他的存储表的设计)可以确定你的代码在哪(物理地址)。
ps:一般的分页会分的很小。如果分的很大。就和上面提到的固定分区分配一样了。非连续是将代码分为很多部分进行存储。
这里存在一个问题:代码存在不被合理分割的情况。比如:x=x+1;他由几条指令构成。但他可能在中间就被截断了。
计算物理地址:
十进制计算公式:
二进制计算方式:
总结:几位的存储空间,根据分区,前几位就是页内偏移量,出去表示业内偏移量的部分,就是他的页号
页表:
页表往往会省略页号,因为程序划分为页号是顺序切割,且页的大小相同,故默顺序查找就能找到对应的页号。
总结:
补充知识点:
基本地址变换机构:
计算机在运行程序时,最开始是创建程序PCB,PCB中会有很多与程序相关的数据其中包括了页表的起始值和页表长度。ps:程序需要的代码会从外存拉入内存中。然后对一个程序代码存放位置建一个表,便于查询。
当需要运行时,根据PCB的长度要求的去计算在哪一页的哪一个地方。
比如程序的代码长度为2500,第一步会看你申请的超过了没有,如果你要低2600行的代码,他就算超过了。
如果没超过就去查找在那一页的哪一行。
具有快表的转换机制:
局部性原理:时间局部性和空间局部性。(代码解析)
for(int i=0;i<x;i++){
sum=sum+a[i];
}
空间局限性:由上面我们可以分析我们定义了数组a[],在空间上我们每次都会去访问ta的下一个存储单位(这里很难一般不会超过他的总存储块),故在空间上ta被重复访问了多次
时间局限性:我们重复执行了i++等代码,这一类指令被反复执行
我们是否能把这一块代码存在一个更快的读取表中让读写速度更快?
故快表(如:cache)诞生了
这里诞生了几个问题:满了后,怎么更换cache的信息(算法)?读取是内存、快表同时查还是先查cache再查内存(这里不冲突,都可以)。
二级页表:
二级页表,其实就是套娃,你也可以叫TA多级页表。因为原理相同,你愿意的话,三级四级都可以,当然这一做法是牺牲时间换空间。
基本分页式管理存在问题:
1、如果空间过大,分区的大小很小,需要花费很多空间去存储。
2、没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问几个特定的页面
基本分段式管理:
页表存储因为大小固定,TA可能存在分割代码不恰当的问题,故使用段式,让用户按照自己的心意分割。
因为段的大小不固定,他需要单独画个区域
段式的代码查询步骤:
段页式的对比:
段页式存储:
各有优点,可以将TA们结合,故产生了段页式存储
段页式的地址转换:
第三part:
这一部分主要介绍虚拟化技术。覆盖技术和交换技术之所以放在一起,是我觉得覆盖技术其实也像是“实现了内存的扩展”,覆盖技术让代码可以在需要才被写入内存中。交换技术则是在进程的角度将进程替换,也是体现替换的思想。
覆盖技术和交换技术:这里简单介绍
覆盖技术在于判断进程代码的优先级,是否被使用等方面去决定是否出现在内存空间内
交换技术:
虚拟存储技术:
上面提到的空间局限性,本质上我们能不能根据我们的需要去请求和换页?
可以,
故操作的基础是进行了段、页式存储
在上面的技术上做加法,加入对应的存储管理(本章以页式存储为例)
请求分页存储管理方式
上文我们提到,非连续存储功能做加法,具体添加如下:
- 请求调页:在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
- 页面置换:若内存空间不够时,由操作系统负责将内存中暂时用不到的信息换出到外存
思考:这时候是不是原有的存储结构是否还有效?
答案当然是错误的,
一个观念,如果你存一点数据其实本身体量不算大,至少单人计算机的存储数据不算大(当然做项目也需要注意企业级优化),
那么我们应该怎么去设计?
从计算机的角度我们去思考?
第一问:我要的在不在内存中?(在,页表显示具体位置,不在,就继续下一问)
第二问:我可以在外存的哪里找到ta(返回一个外存地址)
第三问:我们要把谁换下去?(页面置换算法,算法问题)
第四问:被换下去的有没有被修改过?修改过我又该怎么办?
根据上面我们先去设计我们的数据结构
计算机查找需要的代码的逻辑:
增加了判断在不在内存内,然后在外存哪里可以找到
页面置换算法:
最佳置换算法:(不可能实现,看看就好)
算法核心是根据后续代码那个页面最晚被访问(或不被访问的)进行调换,但实际执行中计算机无法判断谁最后被访问。
先进先出置换算法(FIFO):
最简单,最先进入最先出去,但性能差。
最近最久未使用置换算法:
算法核心:为每个内存块设计一个计数器,计数器的最大值为页表,每次访问
分两个情况:不缺页:计数器全部加一
缺页:把最大的换下去,且被换下去的计算器数字设为零,其他加一
时钟置换算法:(不可实现):
算法核心:通过循环页表,每次对下一位,若为访问位(访问位表示最近有无被访问过)1,则设为0,直到找到0,将这个页面转换出去
给程序分配多少内存块?
三个策略:
固定不变,分多少就是多少。
可变分配局部置换:我先分你一定内存块。然后根据实际情况再分配/收回页面
可变分配全局置换:缺页就换下页面,范围是整个内存
从外存哪里换入和换出?
改过数据的在读写速度快的对换区,数据固定不变的在读写速度慢的文件区
抖动:
实例:如果你的手机内存很小,且喜欢切屏,就出现过有时候切出去切回来不是你原先在干的事,比如你在看视频,切屏切回来就回到首页了