操作系统内存管理的目标
逻辑地址:启动后,OS逻辑划分物理内存。用户程序在运行的时候,只考虑逻辑地址,不用考虑物理地址。
逻辑地址和物理地址的关系:CPU中ALU需要读取逻辑地址的内容,CPU中有个硬件MMU,MMU中有个地址映射关系表,负责转换逻辑地址和物理地址。CPU中的控制器把得到的物理地址和控制信号送到总线上,内存发送物理地址的内容给CPU。(操作系统做了什么等后面再补充)
保护:多个进程空间互不干扰
地址安全检测:用OS有个表,执行前检查起始地址+访问长度是否超范围。确保每个进程访问的地址空间合法。
共享:进程可以访问相同内存
虚拟化:更多的地址空间
虚拟内存:内存不够用,将磁盘用于虚拟内存。
在操作系统中管理内存的不同方法
重定位:链接后确定了代码相对地址,将程序.out载入内存,确定代码的逻辑地址。
分段:代码、数据、堆栈分开存储
分页:内存分成内存页4K,基础访问单位
虚拟内存:按需分页虚拟内存
连续内存分配所涉及的问题:
内存碎片问题:
动态分区分配:
包括分配连续区间来加载未运行的程序,以及分配区间给已运行的程序
分配策略:
需要维护的数据结构:所有进程被分配的分区,空闲分区
碎片整理:
连续内存分配存在碎片问题,而且动态分配内存修改困难。
非连续分配的目标:提高内存利用效率和管理灵活性,比如允许程序使用非连续的物理地址空间,允许共享代码和数据,支持动态加载和动态链接。(多个程序引用同一个库,这个库只导入一次)
非连续需要解决的问题:
逻辑地址和物理地址之间的转换。
解决办法:软件实现(灵活、开销大),硬件实现(够用、开销小)
如何选择非连续分配中的内存分块大小
段式:分块大,要求每个段的段内物理地址连续,不同段可以放到不同的物理地址上
页式:分块小,要求页内的物理地址连续,不同页可以放到其他地方
帧Frame{ 帧号+偏移量 }:把物理地址划分为大小相同的基本分配单位。
页Page{ 页号+偏移量 }:把逻辑地址划分为大小相同的基本分配单位。
Frame和Page的大小要求相同,偏移量要求相同,页号和帧号可以不同。
页表:{页号+帧号}:Frame和Page之间的转换关系。页表随进程运行动态变化。
访问过程:页表基址寄存器存放了页表的起始位置。CPU根据页号和偏移量,在在页表中找到对应的帧号,算出物理地址。页号可能没有对应的帧号,因此页表中还有标志位来表示映射关系是否存在。另外还有修改位和引用位,是否被修改,是否被访问。
缺点:访问一个内存单源需要两次访问,另外,如果内存很大,页表也会很大。
解决:
在段式存储管理基础上,给每个段加一级页表,逻辑地址= { 段号+页号+偏移量}
在寄存器中找到段基址+段号=段表项,其中有页表的基址。页基址+页号=页表项,其中由帧号的基址。帧号+偏移量=物理地址。优点:便于实现内存共享。共享段指向同一个页表,通过相同的页表基址,实现内存共享
需求:进程并发量大,导致内存不够用。要求在小内存中运行大程序
解决:在非连续存储的基础上,将部分内存中的内容放入外存
覆盖:应用程序手动(得自己写)把需要的指令和数据保存在内存中。
交换:操作系统自动(操作系统写)把暂时不执行的程序保存到外存中
虚拟存储:以页为单位自动装入更多更大的程序
问题:一个程序的内存空间不够用
覆盖:将程序划分为若干功能相对独立的模块,将不同时执行且大小相近的模块共享一块内存区域。
换入换出的基本单位:程序内部的模块
具体:将程序划分为必选和可选项,必选部分常驻内存,可选部分在用时放入内存。
缺点:编程难度增加
问题:并发情况下,正在运行的程序内存不够用
交换:将暂时不运行的程序放到外存。
换入换出的基本单位:整个不运行的程序的地址空间
换出之后再换入需要采用动态地址映射。
例子:二维数组有从上到下和从左到右两种遍历方式。如果是从上到下,且数组特别大,按页存储,在访问的过程中会N次缺页,处理效率大大降低!
虚拟存储的目标:只把部分程序放入内存,就让程序运行。且操作系统自动完成
装载时:只将当前指令执行需要的部分页面或段装入内存
缺页/缺段中断:执行中需要的指令或数据不存在内存中。CPU通知OS调入相应页/段
保存:将内存中不用的页/段放入外存。注意:应能方便找到在外存的页。Linux专门设置了对换分区。另一种是采用特殊格式的文件来存储
实现方式:
虚拟页式存储
在页式存储管理的基础上,增加请求调页和置换算法。页表项增加了几个标志位:驻留位:判断该页是否在内存中;修改位:判断当前页是否被修改过,若修改过,置换时需要将修改的内容放到外存中,否则置换时直接作废即可;访问位:判断当前页是否被访问过,不常用的页需要被放入外存中。保护位:表示该页允许的访问方式:rwx。
虚拟段式存储
置换算法:缺页异常,需要调入新页而内存已满,就需要置换
要求:页面调入调出次数少,调出未来不用的页面(难点:如何估计?)
页面锁定:必须内容常驻内存,不可被置换
置换算法评价方法:记录进程访问内存的轨迹,模拟每种页面的置换行为,记录产生缺页的次数,越少性能越好
局部置换算法:分配给进程的物理页面数确定,置换只局限在该进程占用的物理页面内
全局置换算法:全局置换算法位进程分配可变数目的物理页,选择范围是所有可置换的物理页面。
CPU利用率和并发进程数的关系:进程数少,CPU利用率低,增加并发进程,导致内存访问增加,降低CPU利用率。当并发进程增加到一定数量,再增加会导致CPU利用率迅速降低。
工作集算法
工作集:假设当前时刻为t,时间窗口大小为α,一个进程在[ t-α,t]的时间段内,使用的逻辑页的集合。工作集随着时间不断变化。进程的不同阶段,对内存的需求都不同。当内存访问的局部区域位置稳定时,工作集稳定,当局部区域位置改变时,工作集快速扩张或收缩,过渡到下一个稳定值。
常驻集:当前时刻进程实际驻留在内存中的页集合
工作集是进程在运行过程中的固有性质。常驻集取决于系统分配给进程的物理页数量和页面置换算法。
通过控制常驻集的大小,影响缺页率:若工作集是常驻集的子集,则缺页少;若工作集发生剧烈变动,则缺页多;进程常驻集大小到达一定数量,缺页率不会明显下降。
缺页置换算法
缺页率=缺页次数/内存访问次数,或缺页平均时间间隔的倒数。影响缺页率的因素:置换算法、物理页面数、页大小、编程方法等。
进程的物理页数越多,缺页率越低。
缺页置换算法思路:
通过调整常驻集的大小,使每个进程的缺页率保持在一定范围内。若缺页率高于上限,增加常驻集以分配更多的物理页面数。若缺页率低于下限,CPU效率低,减少常驻集以减少物理页面数
访存时,设置引用位标志(是否访问)。缺页时,计算从上次缺页时间间隔T = |t_last - t_now|。若 T > α(窗口大小),缺页率低,置换所有t_last到t_now时间间隔内没有被访问的页。若T <= α,缺页率高,增加缺失页到常驻集中。
抖动:驻留内存的进程过多,导致进程物理页过少,不能包含工作集,造成大量缺页,频繁置换,运行速度慢。操作系统需要在并发和缺页率之间达到平衡。