§2 内存管理
文章目录
C0 存储管理
1)功能:
- 存储分配与回收
- 地址转换:可执行文件链接,程序重定位加载,虚实地址转换
- 存储共享与保护
- 存储器扩充:处理存储不足
2)地址空间
:用于寻址内存的地址集合
-
存储管理基础:
- 地址独立:程序地址与物理地址无关
- 地址保护:程序不能访问其它程序的地址空间
-
早期独立地址空间(CDC 6600、Intel 8088):
动态重定位
使用基址寄存器存储起始物理地址,界限寄存器存储程序长度。程序运行时,检查使用的地址是否超出长度,然后加上基址访存。在不使用专用加法器情况下,CPU运行变慢
3)操作系统在物理内存的位置:
-
位于底部RAM:早期大型机和小型机
位于底部RAM,设备驱动位于顶部ROM:早期PC(DOS)
位于顶部ROM/RAM:手机或嵌入式系统
-
位于RAM的系统,当程序错误时可能被摧毁
C1 直接映射管理
1)内存分配:内存中只有OS和单个用户程序,多个进程需要轮流挂起到磁盘。
- IBM 360早期对内存进行分块实现多进程,设置保护键,由CPU中PSW控制,避免进程越界。由于程序使用绝对物理地址,需要转为实际地址。装载器区分程序中可重定位的地址,在装载时进行静态重定位(运行前计算并修改指令)
2)内存访问:
静态重定位
:在程序运行前就由加载器计算出所有物理地址- 优点:无需实时翻译地址,运行较快
- 缺点:
- 无法运行大于物理内存的程序
- 内存利用率低,需要与磁盘IO降低效率
- 难以实现多进程
C2 分区式管理
1)内存分配:将内存划分为多个分区,OS使用其中一个分区,进程使用一到多个分区
固定/静态分区分配
:分区大小固定- 分区管理:单一队列分配:进程在一个队列中等待分配;多队列:按内存需求大小分队列等待分配
- 优点:易于实现,开销小
- 缺点:内碎片;分区总数固定限制并发程序数
可变/动态分区分配
:分区的边界可以移动,即大小可变- 内存管理:位图或者链表
- DOS将空闲区组织成空闲区链表,每次从足够大的空闲区分配出内存。若分配后产生小于
u_size
的空闲块,则不进行分割,而是全部分给进程。 - 伙伴系统(Linux):空闲块大小为2的幂次。在分配时将一个大储块等分为两个‘伙伴’,回收时,与伙伴分区合并。利用二进制数寻址特点加速合并,释放速度快。内部碎片严重。不如段页虚存技术。用于Linux和多处理机系统
- DOS将空闲区组织成空闲区链表,每次从足够大的空闲区分配出内存。若分配后产生小于
- 优点:无内碎片
- 缺点:外碎片;
- 内存管理:位图或者链表
2)页面碎片
:
- 内部碎片:已分配区域中未装满区域,无法被整理,最后会释放
- 外部碎片:动态分区管理留下的分区间间隙, 是导致内存系统性能下降的主要原因,可以整理清除
内存紧缩
:移动物理内存中分散的分区拼接成连续的大分区。用于腾出大空闲区,需要动态重定位技术支持。
3)空闲区管理
位图
:内存分割为n个分配单元,每个单元对应位图中一位,若被占用则该位为1。- 优点:位图的大小由分配单元的大小和内存总量决定(1/n),故可用固定大小的内存空间进行记录(空间开销固定)
- 缺点:分配内存时需要耗时查找连续的大小适合的空闲区;分配单元标志位可能出现错误,无容错能力
空闲区链表
:(双向)链表数据 : 状态标志(空闲或进程)+地址+长度- 有容错能力:被占空间和闲置空间相互验证
- 若将链表分为空闲区和进程区链表,可以增加搜索速度,但降低了释放速度(从进程区链表删除并有序插入到空闲区链表)
4)空闲区适配算法:
- 首次适配算法:按地址顺序查找第一个大小充足的空闲区
- 下次适配算法:首次适配每次都从头开始找,下次适配从上次找到的位置向后找。实际上性能变差。不会使小空闲分区集中到一端,但导致缺乏大分区
- 最佳适配算法:查询整个链表找到能够容纳当前进程的最小空闲区,查找速度慢。产生了更多更小的空闲区。
- 最差适配算法:与最佳适配算法相比,分配最大空闲区,故导致缺乏大分区。
- 快速适配算法:为指定大小的空闲区分别维护链表。不利于回收时合并相邻的空闲区。若不合并则将产生大量小空闲区。
※ 额外分配内存:当进程段有增加内存占用的可能时,应在最初时额外分配内存间。若有2个增长的程序段,可以使一个向下增长,一个向上增长
5)内存空间不足:
-
覆盖技术
:(早期OS)将程序划分为功能相对独立的程序段,执行时不需要同时装入的段组成一组,在内存不足时组内的段可相互覆盖- 要求作业各模块中有明确调用结构。程序需告知OS覆盖结构,增加编程负担。
- 覆盖减少了一个程序运行所需的空间
-
交换技术
:通过将进程挂起到磁盘实现多进程内存分配。内存不足时将其他进程挂起,否则只能挂起该进程。(直到有连续的空闲空间可供分配)- 增加了系统开销和处理任务。没有考虑到地址访问的统计特性
- 增加了并发数量
-
交换技术用于多进程管理,覆盖技术是对单进程管理。
C3 虚拟内存
P1 虚拟内存技术
1)作用:
- 优点:
- 为每个进程提供了大的,一致的连续私有地址空间。逻辑地址连续但实际物理地址可不连续
- 将主存作为辅存的高速缓存,提升内存效率
2)虚存空间与物理空间:
-
虚存空间: 用户编程序使用的地址称为虚地址或逻辑地址
-
物理内存空间:计算机内存访问地址称实地址或物理地址
-
地址转换:MMU(存储管理单元)
1° 检查页号是否越界
2° 页表定位:页表起始地址+页号*页表项长度
页表寄存器
CR3:保存页表在内存中首地址及页表长度3° 查询页表:读出实页号
4° 物理地址:实页号+页内偏移
3)局部性原理:空间局部性(访问于小范围)和时间局部性(同一数据短时间被重复访问)
4)内存管理:
- 实存管理:分区、分页、页式、段页式
- 虚存管理:请求分页、请求分段、请求段页
P2 页式存储
1)页式虚拟存储:虚存空间和主存空间按固定大小分页,称虚页
(页面),实页
(页框)。
-
页面大小:
- 小页面有利于减少内部碎片,节省内存,但需要更大的页表,使用更多TLB表项
- 使用磁盘进行存储,则大部分的IO用于寻道和旋转,大小页面的传输时间基本一致。
- 估计合适的页面大小:P=√2se,s为进程平均字节数,e为每页表项字节数
- 小页面有利于减少内部碎片,节省内存,但需要更大的页表,使用更多TLB表项
-
页表
:以虚页号为索引记录实页号,每个程序都有独立页表。页表大小=虚页数×页表项大小(注意单位) -
页表项
:实页号 + 修饰位- 有效位:表示是否存在内存
- 读写位:控制读写权限
- 高速缓存禁止位:用于IO时绕过Cache直接访问设备,用于采取内存映射的IO设备
-
多级页表
:破除了页表存储的连续性,避免将全部页表项存放在内存当中。上级页表将下级页表的实页号作为页表项,查表时逐级查询。 -
哈希页表
:维护一个散列表。在进行地址转换时需要搜索整个倒排页表,使用Hash链表加快搜索,使用虚页号计算Hash值,链表项和TLB表项一致。难以共享内存 -
反置页表
:实页号作为索引,虚页号作为表项,用于巨量虚存空间/减少进程不必要表项储存。页表大小仅与物理内存大小和页面大小有关,与虚拟空间大小和进程数无关(位数不考虑)。通常HashMap<虚页号,实页号>使用。很难实现内存共享
2)快表
(TLB,转译后备缓冲器)
-
作用:加速虚拟存储器访问页表(原来每次多次访问页表,一次访问数据),实质是页表的高速缓冲
- 若数据地址在快表,一定在页表中,若不在页表,一定不在快表;若数据不在页表,一定不会在Cache中
-
实现:用Cache(一般是全相联)存储部分活跃的页表项,存储在MMU当中,查表时进行并行匹配所有快表项。
-
内容:虚页号(Tag)+ 对应实页号(组相联应该换为组内块地址),有效位,修改位
-
ASID
:进程标志,用于识别不同进程的表号避免干扰。若不支持包含多个不同ASID条目,每次上下文切换都将冲掉所有TLB表项 -
TLB失效:OS到页表甚至硬盘当中找到实页号,替代一个TLB表项
- 软失效:页表项在页表中而不在TLB
- 硬失效:页表项不在页表
3)缺点:
- 难以满足动态需求(栈空间增长和动态链接)
- 不便于数据共享和保护(以页为单位导致)
- 纯分页系统不支持页面置换,必须将所有页调入内存
P3 页目录自映射
1)页目录:记录二级页表地址映射,1K个二级页表逻辑上连续,物理上可以分散。页目录占1页(4KB),1K项每项对于一个二级页表(每页映射4M,也是4K)
3)页目录自映射:描述页目录及页表在虚存空间中组织形式
- 记页面大小4K,在4G虚拟内存空间中
- 一个页表占1页,一共1K项,每项映射4KB页面,总共映射4MB页面。所有的页表一共也是4MB
- 一个页目录占1页,含有1K项,每项映射一个页表地址,总共管理4GB
- 将4MB页表连续存储并4MB对齐(低22位为0),这样就有一张页表的内容和页目录完全相同,同时有一个页表项指向自身。(压缩映射原理)
4)PD:页目录,PT:4M页表,PDE:页表项
VPDbase = VPTbase + VPTbase>>10 (第VPTbase>>12个目录项,而每个目录项4KB)
VPDEself-mapping = VPTbase + VPTbase>>10 + VPTbase>>20
5)构造一个虚地址与实地址完全相同的4M空间:在2M开始分配2个4K页面,低4K是页表,高4K是页目录
P4 请求页面式管理
1)缺页中断\异常
:访问的页面不在内存当中
2)交换分区\文件
:按页划分的连续磁盘空间,用于页面换出
3)页面调入:请求调页
- 进程创建时,并不调入对应的数据和代码段
- 在产生缺页中断后,再调入请求的页
- 现场保护
- 页面定位:通常保存在硬件寄存器中,否则需要索引PC分析地址
- 权限检查:检查访问
- 置换页面,旧页面写回,新页面调入。等待磁盘IO中可进行进程调度出让CPU
- 更新页表、Cache、TLB
- 恢复现场
- 预调页:不同于请求调页,预调页为进程维护页面工作集,事先调入集合中的页
4)页面置换算法:
-
最优页面置换(OPT)算法
:之后最晚访问到的页面换出。- 除非仿真运行得到访问时间,否则不可实现。
- 通常只用于比较置换算法性能
-
最近未使用(NRU)算法
:优先替换未访问页面,再替换未修改页面。- 为每个页面分配两个修饰位R(访问)和M(修改)。定期(如时钟中断)将R位清零。
- 若硬件没有R位,可以使用OS模拟:OS创建一个内部表记录RM,切换进程时将R位全部清零,则访问页面时一定会发生一个中断,设为只读,R置位,修改页面时同样在中断处理时设为读写,M置位。
-
FIFO算法
:维护一个队列,记录每次访问的页面,先进先出- 改进算法
二次机会算法
:若队首R位为1,将R位清零后放到队尾。否则作为置换页面时钟算法
:二次机会改进:变成循环队列,维护队首(时针)即可
- FIFO类算法实现简单但命中率低,一般很少使用。
- Belady异常:分配页框增多,缺页率反而提高
- 改进算法
-
最近最少使用(LRU)
:最近访问过的页将来被访问的概率大- 硬件实现:使用每次执行指令时递增的64位计数器C模拟时间,每次访问页面,将C的值保存到页面的修饰位上。
- 软件实现:
- 法一:每次将访问到的页面移动到链表头
- 法二:将NRU中R位加到一个计数器
- 改进:老化算法,设置移位计数器,每次将R从左侧移入
-
工作集算法
:工作集为一段时间内进程k次访问的页面集合。使用"预先调页"将工作集全部载入内存。- 近似处理为过去t秒实际运行时间(CPU时间)内访问的页面,寻找R为0且生存时间少于t的页面换出。若R全部为1,找M=0的页面换出。其历史变化未必预示未来的变化
-
工作集时钟(WSClock)算法
:略
5)一致性问题:
-
若换出页面是磁盘中的文件,且已经修改,则应该写回文件;
若未修改,则直接丢弃(因为已有存在的副本);
-
若为进程数据,写入交换空间。
如果交换空间已有该数据(即自上次换出后未修改)则抛弃
6)页面分配策略:
- 固定分配策略:分配固定数量的页框
- 可变分配策略:页框数在其生命周期内可变
缺页中断率(PFF)
:用于动态分配页框(驻留集),使之控制在可接受的水平- 优点:灵活,提升吞吐量和内存利用率
- 缺点:统计缺页率增加系统开销,难于确定调节阈值,需要撞门的软硬件支持
7)页框置换策略:
-
局部策略:在进程自身驻留集中判断是否有空闲页框
-
全局策略:在整个内存空间中查找空闲页框,允许从其它进程驻留集中选择。全局策略的缺页率受到其它进程的允许状况影响。
-
固定分配策略只能用于局部置换策略。可变分配策略则都适用。
-
LRU、FIFO等算法选择局部或全局策略都可以。而工作集算法较结合局部置换策略能发挥更好的性能
-
内存的初始分配
- 平均法
- 比例法
- 优先级
P5 系统性能
1)负载控制:控制并发运行的进程数,减少抖动
抖动
:随并发程度上升,处理器利用率先上升再下降(驻留集小于工作集)- 消除和预防:使用局部置换策略/工作集算法(微观)/预留页面/挂起进程(宏观)
L = S 准则
:缺页周期 = 缺页中断处理时间时处理机利用率最大50%准则
:分页单元利用率在50%左右,处理机利用率最大(若使用时钟法及全局置换策略可以监视时钟移动速率来调整系统负载,移动快则减少进程数)- 选择挂起进程:优先级最低进程/缺页阻塞进程/最后一个被激活进程(页面少开销小)/驻留集小进程(再激活开销小)/大进程(释放空间多)
2)页面清除策略:即写回策略
-
页缓冲/后备存储
:缺页中断发生时,写出页面保留在内存缓冲区,等待批量写回- 可能通过该缓冲区找回被置换的页面
- 使用链表组织,分未修改和已修改页面
-
页面锁定:对于正在进行I/O的页面需要锁定避免被换出/换入
- 另一种方式是在内核中完成I/O后再复制到用户区
3)有效内存访问时间:
EAT = (1-缺页率)×命中访存时间+缺页率×(保存+换出+换入+恢复)
4)策略机制分离:Mach
- 存储管理系统分为底层MMU + 缺页中断处理 +外部页面调度程序(用户空间)
- 程序启动时通知页面调度程序建立页面映射,分配后备存储。缺页中断时,内核告知需要的虚拟地址,由调度程序取回到自己的空间,中断处理程序再清除调度程序中该页面映射,最后由MMU处理程序放到用户空间中的正确位置。
- 页面调度算法可以放在外部或是调度程序中,放在外部则无法直接访问R、M位。
- 优点:模块化编程
- 缺点:多次切换用户态和内核态以及模块间通讯引起性能降低
C4 页面共享
1)写时复制
:两个进程共享同一块物理内存,其页面标志为写时复制,在发生写入操作时,复制页框再写入,并更新写入线程的页表。
2)内存映射文件
:进程通过一个系统调用(mmap)将文件映射到虚拟空间上,对该文件的访问不通过文件读写方式而是数组访问方式。
- 映射共享的文件只在被访问时才会读入
- 进程退出或解除映射时,被修改页面会写回文件
- 多个进程映射到同一个文件时就可以通过共享内存共享数据,可作为高带宽通信通道。共享库可通过该方式实现
3)共享页面
:不同进程间共享一些只读页面以节约内存。
-
分离地址空间:将代码空间(I)和数据空间(D)分离
-
在支持分离地址空间的系统中,只需使不同进程的I空间指针指向同一页面即可
-
需要记录被共享的页面来避免当共享的一方退出内存时共享页面也被换出
-
共享库(Windows中为ddl或动态链接库):链接器链接时,不将共享库的函数静态链接到可执行文件,而留下一个在运行时能够绑定被调用函数的存根例程(stub routine)。根据不同的OS和配置文件,OS可能在运行进程时同时动态加载共享库或者在第一次调用时加载。
- 缩小了可执行文件的大小,便于修改程序时的重新编译,且节约内存
- 为了避免不同进程中共享库程序位置不同,导致跳转位置不同而阻碍共享,在编译时使用特殊参数告知编译器不产生使用绝对地址的代码而是用相对寻址代码(称位置无关代码)
C4 段式管理
0)页式共享:
- 一个页面可能存在两个不同子程序的段的代码,不应当直接共享
- 页式管理难以动态增长
1)段:长度可变的独立地址空间,独立增长。实质是二维地址空间
- 页是数据存储单位,段是信息的逻辑单位。分段在编程或编译时按信息性质划分
- 寻址:段表,按段号和段内地址寻址。有段基址和段长寄存器防止越界
- 优点:
- 易于共享和保护
- 支持动态增长
- 缺点:
- 需要增加段表和地址转换机制
- 需要结合内存紧缩技术,减少外部碎片(棋盘型的)
- 管理不定长的分段需要交换技术
- 段长受到主存可用空间限制
2)段页式存储管理:分段管理虚存,分页管理实存。
-
地址 = 段号+段内页号+页内地址
-
查询:3次,先查段表得到页表基址和页表长,再查页表得到数据位置,访问数据。
-
MULTICS:
- 为每个程序提供218个最大为216字(36b字)的段,每个段视为一个虚拟内存并对其分页
段表:由描述符(表示对应的段是否在内存中,若段在内存则其页表也会在)组成,描述符包含指向段的页表的指针(18b,使用时补6个0即为24为物理地址),以及段大小(9b)、修饰位,在外存中位置由缺段处理程序的另一个表储存。 - TLB需要提供段号比较
- 为每个程序提供218个最大为216字(36b字)的段,每个段视为一个虚拟内存并对其分页
-
Intel x86:
-
214个段,每段220字(32b字)。
-
x86-64本机模式下可模拟分段系统,但x86-64以后认为这项技术是落伍的
-
核心部分是2张表
局部描述符LDT:描述程序局部信息,包括代码,数据,堆栈
全局描述符GDT:描述OS信息[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t6EsCHmL-1590992781256)(E:\Typora笔记\文件图片\x86段页式.jpg)]
- Li或者Limit表示段长度
- 基址被分为3部分是为了和24位基址的机器兼容
-
选择器:13b索引+1位表示LDT/GD T+2位优先级,访问时将选择器装入6个段寄存器之一来选择段(CS寄存器是访问代码段,DS寄存器访问数据段)。
-
寻址:
- 描述符表头+选择器的索引 = 指定描述符的位置,取出对应的描述符(LDT/GDT)装入微程序寄存器中便于快速访问。
- 检查越界,将偏移量与32b段基址相加成线性地址。
- 若禁止分页(全局控制寄存器中的一位),基址就被解释为物理地址。
- 否则被解释为虚拟地址,查询段页表(CR3寄存器中)来访问。使用二级页表机制。每个程序有一个页目录。通过全局寄存器定位。
-