文章目录
一、小知识
-
socket
是指套接字,原义是接口。 -
Agent
:智能体 -
Architect
:架构师 -
每个进程都有一个系统给定的权限,不能做超出自己权限的事。
-
cpu,内存,文件:是操作系统最具代表性的资源。
-
逻辑地址(虚拟地址,偏移地址) 这个地址都是由cpu发出的地址(程序运行的时候,我们看的到的地址)
-
物理地址: 真实存在的,计算机里实实在在的内存,从0开始。
-
MMU内存管理单元: (Memory Management Unit)作用:把逻辑地址映射到物理地址,是CPU内部的一个小部件。
-
cpu只能访问(内存/寄存器)。所以对内存的管理很重要。
-
内存管理的主要问题:给每个进程分配内存的时候,内存怎么放?放多少?
二、内存分配策略
寄存器
- 每个进程的寄存器中都包括
Base
(基质寄存器和Limit
(内存地址上限寄存器)
- 当进程发出访问地址请求的时候,就要做两次验证,这就是安全保护的基本策略,限制了访问范围只能在(base-limit)。
- 但是这样每次访问都要验证,会带来很大速度时间延迟,怎么解决呢?
- 每一个进程,每个程序的每次运行,被放在内存中的位置是不固定的。这怎么解决呢?
这就涉及到一个,指令和数据在内存当中地址绑定的一个问题。
加载时刻(Load time)
Load time
(加载时刻)编译器编译运行的时候,并不知道程序会被放在内存的哪个位置。我们可以通过设定一些标记。当程序被加载到内存的时候,这个加载程序(loader)知道要放在哪个位置,放入之后,他就对这个程序里的所有(对地址访问的代码的数值)进行一个完整的修改,修改为目前放的位置。这个策略(由loader做动态的复制)很好的解决了多进程同时存在的问题。
执行时处理(Execution time)
- 最好的策略:
Execution time
(执行时处理)不仅可以在内存中放多个进程。而且这个程序已经在内存的时候,甚至可以挪动他。但需要硬件支持,通过修改base的值,就可以实现挪动进程在内存中的位置。
三、内存管理策略
- 目的是内存空间利用率最大化,尽量让他放更多的进程,为了做到这一点,设计了很多策略
动态加载(Dynamic Loading)
- 解决的问题:程序运行到退出,并不会把所有的代码都执行一遍,总有一些用不到的。那为什么要把所有的程序代码都加载到内存呢?动态加载 就可以很好的解决这个问题,只加载要用的部分,不用的部分不加载。这样使得内存空间利用率获得很大的提升。
- 局部性原理
- DLL:动态链接库(Dynamic Link Library),可以看到我们下载的许多软件中,有很多后缀名为
.dll
的文件,这就是动态链接库。使用有很多好处。
①需要使用某个功能的时候,就去加载这个.dll
文件。这就实现了动态加载。
②很好的实现了共享,他在内存中只存在一份,但是很多程序都可以使用他。
③很容易更新,windows主要更新的漏洞就是这个.dll
文件。
交换技术(Swapping)
- 解决的问题:系统中有好多进程在运行,但是有些时候有的进程已经占据了内存,由于各种原因,这个进程属于休眠状态,好长时间不会运行,但是又有进程要进来,却没有空间。这就形成了内存空间的浪费。怎么解决呢?
- 将这个占着茅坑不拉屎的进程写到硬盘上去,这个内存空间就腾出来了。过一会儿时间,它又具备了运行条件,就把他写进来。
- 好处:用硬盘做他的补充,就好像内存变大了一样,可以放更多的进程。
连续分配(Contiguous Allocation)
- 现在操作系统中已经很少用了
- 首次适应算法(使用最多),下次适应算法,最佳适应算法,最差适应算法
- 最大的问题:对内存的申请释放的随机性, 无法很好的适应它,就不能保证用量很好。
- 对编程人员:对内存的使用可以是随机的,也可以自己来设定,总结出来规律之后,就能更好的管理这块儿空间。但是你申请到了内存,其实你并没有得到她,当你真正访问它的时候,才得到
- 碎片(外部碎片&内部碎片)
四、页式存储管理(重点)
- 内部碎片:页式存储管理是产生外部碎片的罪魁祸首。因为计算机分配内存是按照2的指数次幂个字节来分配,比如说,4字节,8字节,16字节,但是进程有的时候用不完这些,比如只需要5字节,就会多出来3字节,这就成了碎片。
- 特点:
①非连续性:(物理空间非连续)进程在物理内存中占据哪些内存,是不连续的,会把进程切成小碎片,散落在各个区域,(逻辑空间非连续)
- 从图中可以看出,进程在内存中保存的位置是散乱的,这样带来的好处就是,每一个页面都会被利用到,不会产生外部碎片,使得物理内存管理起来非常方便。但是这样做还是很麻烦。
- 为什么页面大小是2的整数次幂?
如果不是2的整数次幂,就没有办法计算偏移地址,没办法从中间分隔开, 神奇的二进制。
-
页式存储管理:也许这图只有我自己才能看懂吧,口头概述加深记忆,从逻辑地址 中拿出编号,写成四位二进制数,因为每页是四个字节(2的2次幂),所以要从第二位切一刀,(如果每页面是八字节,2的3次方,就要从第三位切)前面的是虚页号,后面的是页内偏移地址,根据虚页号,在页表中找到对应的物理页号,在将物理页号写成二进制数,加在虚页号前面,拼接成一个新的二进制数,然后再转化为十进制数,根据得到的数字,就可以找到物理地址 中对应的数据
-
这个页面越大越好吗? (页面的大小是2的整数次幂)
①页面小的话,内部碎片越少,但是会增加页表的大小(有多少个页面,页表里就要有多少个项),每一个页表项都是要占空间的,他在内存中。这样的话虚拟地址到物理地址的映射就会慢。
②页面大的话,内部碎片就变多了,页表会减少。虚拟地址到物理地址的映射会很快。
③折中(4k的页面用的多),所以要看使用情况的不同了,对大进程来说,页表的体积就会缩小,从内部碎片来看,进程也不多,浪费就浪费吧。
④在未来开发或者管理过程中,都可以通过改变页面大小调优,从而提高系统效率,内存使用更充分。(调整一定要遵从硬件的架构)
-
页表是怎么实现的?
在内存中,有两个参数,放在寄存器中,一个叫页表基址寄存器(指向页表起始位置),页表长度寄存器(页表的长度),这两个地址都是绝对的物理地址,不跟虚拟进程有任何的瓜葛。 -
问题:数据,指针每次都要访问两次内存。(先进内存查页表,得到物理地址,再进内存中查到该数据。)怎么解决?
用TLB(ranslation look-aside buffers) 快表—相当于硬件的cache,思想也是跟cache一样(加快访问内存的速度)。cache是将当前最热的数据放在里面。TLB也是如此,把你当前用到的,最热的拿过来放入表中,就省去了根据虚页号去查物理页号。
- 在快表中,存储的多了,查找起来也费时间,那么怎么加快查找速度呢?
使用硬件并行搜索 (涉及到电路问题),软件实现并行很麻烦,但硬件很简单。只用查找一次。如果一次没找到(未命中),就会去普通的页表查找,并且更新到快表中。
内存保护
- 如何实现多个进程互相之间不能访问到彼此的内存?
对指令严加把控,读,写,执行。Valid-invalid bit(有效无效位) 有效V即可访问。无效i触发一个中断,操作系统意识到这个进程执行了一个非法操作,就会杀死该进程。
共享内存
- 在页式存储管理中,实现内存共享是简单的。该共享的就共享,该私有就私有
- Copy-on-write(写时复制)
两级/三级页表结构(Two-Level Page-Table Scheme)
- 由外层页表算出来,对应的是哪个项,再根据这个项,找到对应的内存页表,再根据虚页号,找到物理页号,进而找到对应的实际物理地址。
- 4k物理页面的页内偏移是12位(因为4k=2的12次方),剩下的20位属于页面号。然后再将这20位页面号分割成两部分。
-
在根据p1(前面页面号)和p2(内存页面号),查找。
-
64位系统如果采用两级页面的话,就要维护42位的前面页面号。这是一个很大的开销,所以出现了三级页表,再将页面好进行划分。64位系统的开销比32位的大。现在的64位系统采用的就是三级页表。
哈希表
- 页表耗费的时间还是挺大的,出现了哈希表,这样在内存中维护的就不是页表,而是哈希表。但是哈希表存在碰撞。
反表(Inverted Page Table)
-
这是64位系统使用的方法,因为我们实际上要管理的是物理内存,如果把系统中所有进程的所有列表项放在一起看的话,这些页表项里,有用的,有价值的,只是那些和物理内存相对应的,没有对应关系的页表项是没有存在价值的。如果我们把所有没价值的列表项都扔掉,那么页表的体积就可以得到控制了。控制到:有多少个物理内存,有多少个物理页面,那么就有多少个页表项,一一对应。
-
全系统中只有一张页表。但是!!! 这种方法cpu不支持,我们现在用的方法就是多级页表。因为要兼容32位,64位。
五、段式内存管理(Segmentation)
- 把程序分成若干段,程序段,代码段,栈,等等等等。在进程的逻辑空间中,自己划分出若干块,每块放不同的东西,执行不同的功能。
六、段页式管理
- inter的cpu做得非常强大,你可以段式管理,页式管理,也可以段页式管理。
- 这是,我们目前cpu里面的机制,每执行一个指令,就要执行这个过程。