讨论内存管理之前,首先要搞清楚内存中到底存放的是什么东西?内存中存放的是进程,内存中各个进程圈地而治,所圈的地盘中存放各个进程自己的指令和数据,进程执行完毕后,退出内存,还出圈给它的地。如何给各个进程圈地,就是存储管理的核心。
计算机的存储技术可以从两个维度来分类:
连续还是离散?
简单还是虚拟?
连续还是离散:
连续存储技术,程序一定是被连续存储的。离散存储技术,程序可能是被离散(分散)存储的。
简单还是虚拟:
简单存储技术,即将程序所需的全部数据全部装入内存。虚拟存储技术,即将程序当前所需的数据装入内存,后面需要的数据后面再装入内存,即一边执行、一边装入。
分区技术是连续的, 一个进程占有的存储空间是连续的。每个进程所占的分区的起始地址、偏移量(区间大小)等信息会记录在进程的PCB中。分区存储技术分为两种:
固定分区
动态分区
固定分区:
系统启动时将整个内存空间分为多个固定大小的分区,会存在大程序装不下(使用内存覆盖技术解决),小程序存在内存碎片的问题。
动态分区:
将内存空间按照进来的程序量身定制的进行分区,但是随着时间的推进,还是会出现内存碎片,相较于固定分区技术来说只能说减小了浪费,但并未完全消除浪费。
可以使用“紧凑技术”,像挪动积木一样,将小碎片“滑”到一起,重新整理后再使用,但是这是个纯物理动作,太过耗时。
分页技术:
分页存储技术是离散的,一个进程占有的存储空间可能是离散(分散)的。在分区技术中,由于禁锢于程序要连续存放这个执念,因此避免不了内存碎片,导致内存利用率不高。离散型存储技术中创造性的给出了将进程分开存储的理念,也就是分页技术。
分页技术,即将物理内存、进程二者都分为大小相等的一个个小块,进程所分成的小块叫做页,内存所分成的小块叫页框,进程页可以分散的存放在内存页框中。
页表:
由于分散存储,每个进程就需要额外的记录信息来记录某个进程的页分别分散在何处,这个记录信息即——页表,页表每个进程都有一张。
空闲页表:
除此之外还有一张空闲页框表,用来记录空闲的页框,以便于进行分配,空闲页表整个系统只有一张。
页表寄存器:
页表本身很大,不可能装入寄存器,因此页表是放在内存中的,存在一个页表寄存器,用来记录当前所执行的进程的页表的起始位置在内存何处。
地址转换:
即将逻辑地址转换为物理地址。分页系统中的逻辑地址,即当前指令是程序的第几条语句,如第1000条语句,即逻辑地址为1000。逻辑地址除以页框的容量就能算出页框号和偏移量,然后根据页表寄存器可以找到页表的起始地址,找到了页表的起始地址也就是找到了页表,然后根据页表找到页框号,找到页框后再根据偏移量最终就定位到语句实际所存放在的到物理地址。
快表(TLB):
即使使用了页表寄存器来记录页表在内存中的起始位置,但是找到起始位置后接下来查找页表这个动作都是在内存中,CPU要与内存直接交互明显速率会很慢。
快表本质上是个cache,用于加快页表的检索速度。快表中装载的就是页表,但是页表太大,不可能全部装入快表,因此快表中装载的是页表中的部分页表项,具体的装入原则遵从局部性原理。
分页技术中其实有些地方是欠考虑的,比如说只按照页框的大小来粗暴的切分程序,有些功能、甚至单个指令被切到不同的页中,并没有考虑到所切分出来的页是否具有意义,比如说单个页是否能完成单个功能(这样就避免了当程序需要完成某个功能时,由于某个功能被切在不同的页,需要查多次页表的情况。)
分段技术就是为了解决上述问题。
分区,单进程的物理地址是连续的。
分页,单进程的分块大小是固定的。
分段,单进程的物理地址是离散的、块的大小是不固定的,一块就叫一段。分段的大小就可以按照功能性来分割,因此各个分段是独立的,对于程序设计而言是十分有利的。但是由于其有动态分区的特性,会使得它存在较为明显的内存碎片问题。
分页中有页表,分段中有段表。
页表记录哪些分页属于哪个进程,页与物理内存的映射情况。
段表记录哪些分段属于哪个进程、每个段的长度、段与物理内存的映射情况。
分页中有页表寄存器,记录页表的首地址。
分段中有段表寄存器,记录段表的首地址。
分页中的地址转换:
逻辑地址是一维的,指的是第几条语句。
由逻辑地址计算出页号和页内偏移量,最终通过页表找到物理地址。
分段中的地址转换是:
逻辑地址是二维的,由CS:IP(段号:段内偏移量)组成。CS(段号)放在CS寄存器中,IP(段内偏移量)放在IP寄存器中,二者共同构成分段系统中一条程序语句的逻辑地址。
之所以不能使用一维的逻辑地址是因为每个段的大小是不固定的,没办法通过计算的方式定位到段号。
页,大小固定不变,不随进程的不同而不同。
段,长度不定,同一进程的不同段长度也可能不等。
分页活动源自于对物理内存管理的需要,是在系统内部进行的,由系统实施,用户感知不到。
分段活动愿意对模块化程序设计的需要,在系统外部进行(用户编写代码的时候自行分模块),由用户实施,用户是知道的。
分页系统,有较高的物理内存使用率,但是不利于数据共享以及模块化的程序设计。
分段系统,存在分区的特性(按需分配,多出现几次内存置换就会产生内存碎片),所以物理内存利用率不高,但是有利于数据共享以及模块化的程序设计。
段页式各有各的优缺点,因此现在主流的内存管理技术是综合二者的——段页式技术。
段页式技术,即从段和页两个层面来划分物理内存,段被划分在多个页中。
段表中记载的不再是哪些分段属于哪个进程、每个段的长度、段与物理内存的映射情况,记载的是段内页表的起始地址,也就是说每个段都有个单独的段内页表,段内页表记录着当前分段被分到了哪些页中。段内偏移量除以页框大小就可以转换为段内页号和页内偏移量。
简单存储存储,将所有数据全部存入内存。
虚拟存储技术,按照局部性原理将部分数据存入内存。
由于虚拟存储是将部分数据装入内存(按照局部性原理装入),装入的数据不够用的时候再装入新的数据到页框中。因此虚拟存储中需要两个技术做支撑:
1.调页技术页
将新的数据载入内存,即将新的页调入页框中。
2.面置换技术
当页框不够时,将老的数据从内存的页框中卸载掉。
虚拟内存中装入的内存的部分页称为“驻留集”,当进程需要的数据不在“驻留集”中时,进程状态转为block(阻塞态),产生中断,转系统调用,启动IO,读取所需要的数据装入页框,数据装入页框后,逐级读入cache,最终数据准备好后,进程状态转为ready(准备态)。
在调页置换整个流程中,存在一种糟糕的情况,即调入进内存发现页框已满,于是置换页面,结果往后执行发现刚刚换出去的页面是现在要用的,如此循环,机器大量的进行IO,系统性能跌落,称为“抖动”。
“抖动”发生的原因常见的有:
1.进程分到的页框太少。
2.系统的负载过高。
其实所有原因的本质都是内存紧张。