目录
1.4.2 动态重定位(Dynamic Relocation)
1.4.4 重定位策略(Relocation Policies)
2.5 转译后备缓冲区(Translation Lookside Buffer)
4.1 地址转换方案(Address Translation Scheme)
4.3 段页式内存管理(Segmentation With Paging)
1.内存管理(Memory Management)
外部碎片(External Fragmentation):
-
定义:指在内存分配过程中,由于内存中空闲块被分割成若干小块,导致虽然总的空闲内存量足够大,但这些空闲块由于不连续而无法满足大块内存请求的问题
-
产生原因:动态分配内存时,内存分配和释放操作会导致内存中出现许多不连续的小块空闲区域。常见情景包括:
-
分配和释放不规则:当进程被创建时分配内存,进程结束时释放内存,经过多次分配和释放操作后,内存中会出现许多大小不一的空闲块
-
首次适配算法(First Fit)
-
-
解决方案:
-
紧凑(Compaction):将所有空闲块集中在一起,形成一个大的连续空闲块,但这种方法需要停止系统运行,开销较大
-
分区分配算法优化:如果best fit或next fit,可以在一定程度上减少外部碎片
-
内部碎片:
-
定义:在分配内存块时,由于分配的内存块比实际需要的内存要大,从而导致分配的内存块中有未使用的部分,形成内存浪费
-
产生原因:内存分配的粒度和进程所需内存大小不匹配,具体情境包括:
-
固定分区分配:内存被划分为多个固定大小的分区,如果进程所需的内存小于分区大小,那么多余的部分就成为内部碎片
-
分页系统:在分页内存管理中,每个进程的内存需求被划分成若干个页,每个页大小固定。如果进程最后一页内存需求小于一页大小,剩余部分就会形成内部碎片
-
-
解决方案:
-
调整分区大小:合理设计分区大小,尽可能匹配常见进程的内存需求
-
混合分配策略:结合分页和分段技术,能够灵活分配内存,减少碎片的产生
-
1.1 存储层次结构(Storage Hierarchy)
内存层次结构:
-
少量快速、昂贵、易失的高速缓存
-
一些中速、中等价位、易失的贮存
-
千兆字节(GB)的慢速、廉价、非易失性磁盘存储
1.2 内存管理器(Memory Manager)
连续内存分配:
-
每个程序的数据和指令在内存中分配一个连续的空间
-
单分区分配
-
固定大小的分区分配
-
可变大小的分区分配
非连续内存分配:
-
每个程序的数据和指令被分配到不连续的内存空间
-
例如:分页内存管理,分段内存管理
1.3 没有内存抽象
1.3.1 没有内存抽象的单道程序
三种组织内存的方式:
-
操作系统在内存的底部的RAM中;用于大型机和小型机;不再使用
-
操作需提供在内存顶部的ROM中;在一些PC和嵌入式系统中使用
-
设备驱动器在内存顶部的ROM中,剩余在RAM中,在早期的PC中使用,其中ROM部分称为BIOS
方法1和方法3的缺点:程序中的bug可能会损坏操作系统
当程序大小大于分配给它的内存/分区数量时,怎么办:
-
覆盖(Overlays, 过去使用)
-
动态链接(Dynamic Linking,现在使用)
覆盖:
-
在内存中只保留那些在给定时间内需要的指令和数据
-
当需要其他指令时,它们被加载到先前不再需要的指令和数据所占的空间中
-
覆盖层由程序员实现,不需要操作系统的特殊支持,覆盖层结构的编程设计比较复杂
覆盖存在的问题:
-
要求程序员在文件结构等的帮助下组织程序的覆盖结构
-
要求程序员指定在不同情况下加载哪个覆盖层
1.3.2 没有内存抽象的多道程序
固定分区多道程序设计:
-
固定分区分配:
-
将主内存划分为一组称为分区的互不重叠的区域
-
每个活动进程接收一个固定大小的分区
-
分区在boot time固定
-
处理器在每个进程之间快速切换
-
分区的大小可以相等,也可以不相等
-
多个边界寄存器防止损坏
-
-
固定分区的缺点:
-
内部碎片
-
外部碎片
-
进程大小限制
-
多道程序设计的度较低
-
可变分区多道程序设计:
-
可变分区分配:
-
程序被放在适合的分区
-
不浪费空间
-
不存在内部碎片
-
-
当进程被删除时,可能会发生外部碎片
-
-
内存和多道程序设计:
-
内存在多道程序设计中需要两样东西:重定位和保护
-
重定位:进程不能被放回同一个主存区域
-
保护:必须由处理器而不是操作系统来保证
-
交换技术(Swapping):
-
使用交换技术来实现多道程序,无需内存抽线
-
交换技术是一种内存管理方法,它将每个进程完全引入,运行它,然后将其放回磁盘,以便将另一个程序加载到该空间中
静态重定位:
-
加载过程中静态修改地址(类似于链接器)
-
优点:不需要硬件支持
-
缺点:
-
减慢了加载速度
-
一旦加载,程序的代码或数据不能移动到内存中,除非进行进一步的重定位
-
加载程序需要某种方式来区分什么是地址,什么是常量
-
1.4 内存抽象:地址空间(Address Space)
将物理内存暴露给进程的主要缺点:
-
如果用户程序可以寻址内存的每一个字节,它们可以很容易将操作系统损坏
-
同时运行多个程序是很困难的(如果只有一个CPU,则轮流运行)
1.4.1 概念(notion)
地址空间:进程可以用来寻址内存(供进程驻留的抽象内存)的地址集。每个进程都有自己的地址空间,独立于其他进程
地址绑定:将程序指令和数据关联到物理内存地址的过程称为地址绑定或重定位
1.4.2 动态重定位(Dynamic Relocation)
需要硬件支持:
-
为CPU配置两个特殊硬件寄存器:基数寄存器(Base Register)和限位寄存器(Limit Register)
-
基数寄存器:地址空间的起始位置
-
限位寄存器:地址空间的大小限制
-
这些值在加载进程和交换进程时设置
优点:
-
操作系统可以很容易地在执行过程中移动进程
-
操作系统可以允许进程随时间增长
-
操作系统只是改变限位寄存器
-
-
简单,硬件速度快:两个专用寄存器
缺点:
-
进程之间不能共享内存
-
进程首先于物理内存大小
-
使内存管理复杂化
1.4.3 地址绑定(Address Binding)
指令和数据到内存地址的地址绑定可以发生在三个不同的阶段:编译时、加载时、执行时
编译时绑定:
-
在编译时必须知道程序在物理内存中的位置
-
编译器或汇编器将符号地址转换为绝对地址
-
加载 = 将可执行文件复制到内存中的适当位置
-
如果开始位置改变,程序将不得不重新编译
加载时绑定:
-
编译器生成可重定位的代码
-
编译器将名称半丁到相对地址(起始地址的偏移量)
-
编译器也生成重定位表
-
-
链接器解析外部名称并将目标文件组合到一个加载模块中
-
加载程序将相对地址转换为物理地址
-
运行期间不允许重定位
运行时绑定:
-
程序/编译单元在执行过程中可能需要重定位
-
CPU生成相对地址
-
相对地址绑定到物理地址在运行时基于翻译单元的位置
-
需要适当的硬件支持(上面说的动态重定位)
1.4.4 重定位策略(Relocation Policies)
取决于何时以及如何将地址从主存的逻辑地址转换为物理地址
静态重定位:
-
程序加载到连续的内存位置,只要有空间,在加载过程中没有重定位
-
在加载时,操作系统调整进程中的地址以反映其在内存中的位置
-
重定位后,操作系统无法移动进程
动态重定位:
-
运行时逻辑地址到物理地址的映射与一些硬件机制的支持
-
硬件将基数寄存器添加到逻辑地址以获得物理地址
1.4.5 交换(Swapping)技术
如果内存中没有足够的空间供所有进程使用,可以交换一些进程以腾出空间
-
操作系统将其完整状态存储到磁盘
-
操作系统可以回收就绪或阻塞进程使用的空间
当进程再次活动时,操作系统必须将其交换回内存
-
如果是静态重定位,需要在同一位置进行更换
-
如果是动态重定位,操作系统可以将进程防止在任何空闲分区(但是也必须基于基数寄存器和限位寄存器)
1.4.6 管理空闲内存(Free Memory)
位图(Bitmap):
-
将内存分成小单元,每个单元对应的是位图中的一个位:0表示单元空闲,1表示单元已经被占用
-
分配时,内存管理器必须找到连续的0位
链表(Linked Lists):
-
列表中的每个表项指定一个孔或一个进程、它的起始地址、长度以及指向下一个表项的指针
位图内存管理:
-
内存划分为多个分配单元
-
0:空闲,1:已分配
-
-
分配单元大小决定效率
-
在每4KB一个块时,我们只需要256位(32字节)每MB内存
-
对于较小的块,我们需要更多的位图内存
-
在位图中很难找到大片连续的空闲内存区域
-
链表内存管理:
-
用链表跟踪空闲/分配的内存区域
-
表中的每个表项都对一个连续的内存区域应
-
表项可以表示已分配或空闲
-
存储放置策略:
-
First Fit:
-
扫描列表,选择第一个满足需求的孔(hole)
-
-
Next Fit:
-
从列表中最后一个请求成功的点开始往后搜索
-
-
Best Fit:
-
使用尺寸与需求相等的孔, 如果没有相同的孔,则使用较大但尺寸最接近的孔
-
问题:经常需要搜索完整的列表
-
-
Worst Fit:
-
使用可用的最大孔
-
-
Quick Fit:
-
为一些更常见的尺寸要求维护单独的列表
-
2. 分页(Paging)
2.1 虚拟内存(Virtual Memory)
对虚拟内存的需求:
-
用户逻辑内存与物理内存分离
-
只有部分程序需要在内存中运行
-
逻辑地址空间因此可以比物理地址空间大得多
-
需要允许页面交换或交出
虚拟内存的实现:
-
分页
-
现代方法,目前最常见的
-
-
分段:
-
早期方法,非常简单
-
可能更适合过程的行为, 尽管它可能更难使用和更难实现
-
-
分段分页的合并
虚拟内存所需的支持:
-
硬件支持:
-
内存管理单元(MMU)
-
转译后备缓冲区(TLB)
-
-
操作系统支持:
-
虚拟内存系统,用于控制MMU和TLB
-
虚拟地址:是一个内存地址,进程使用它来访问自己的地址空间
-
虚拟地址与存储它的物理地址不同
-
当进程访问虚拟地址时,MMU硬件将虚拟地址转换为物理地址
-
虚拟地址和物理地址的对应管理由操作系统决定
MMU:
-
将虚拟地址转换为物理地址的硬件
-
每次内存引用都要经过MMY
TLB:
-
缓存MMU从虚拟地址到物理地址的转换
-
仅仅做一个优化,但很重要,缓存在很多计算机领域很多设计中都有用到
2.2 分页
物理内存:
-
被划分成页面帧(Page Frame),固定大小块
-
大小是2的幂
逻辑内存:
-
被划分成页面(page)
-
与页帧的大小相同
操作系统的职责:
-
维护页表(Page Table):页表是一个将页转换为帧的数组
-
从空闲帧中分配足够的页面来执行一个程序:要运行一个大小为n页的程序,需要找到n个空闲的页面帧并加载程序
-
操作系统保持跟踪所有空闲帧:设置一个页表转换逻辑到物理地址
优点:
-
允许进程的物理地址空间不连续,根据需要分配
-
易于分配物理内存
缺点:
-
内部碎片化
2.3 地址转换方案
CPU生成的地址分为:
-
对于给定的逻辑地址空间和页面大小
-
页码(page number)
-
用作页表的索引
-
页表包含物理内存中每个页的基地址
-
-
页偏移(Page Offset)
-
与基地址结合,发送给MMU
-
物理地址:
-
帧数(Frame Number)
-
页偏移(Page Offset)
2.4 页表(Page Table)
概述:
-
由操作系统管理
-
将VPN(Virtual Page Number, 虚拟页号) 映射到PFN:VPN是表中决定PFN的索引
-
虚拟地址空间中每个页面一个PTE(Page Table Entry, 页表项),每个VPN一个PTE
-
大多数OS为每个进程分配一个页表
-
PTE的结构:
-
前面那段黑的是物理地址
-
Page Frame number:映射frame number
-
Present/absent bit:1/0表示表项有效/无效
-
Protection bit:允许的访问类型
-
Modified (dirty bit):修改后标识并等待磁盘
-
Referenced:设置页面何时被引用(帮助确定victim bit)
-
Caching disabled:缓存用于将来逻辑上属于磁盘的数据保存在内存中,以提高性能
-
设计问题:
-
快速访问问题:每条指令通常需要2个或更多的页表引用,因此快速访问时必要的
-
大内存页表问题:
-
由于内部碎片造成的平均浪费为每个进程1/2页,因此需要较小的page size
-
但是,小的page size -> many pages -> large page table -> page table(for each process) 将占用大量内存空间
-
Solution:
-
快速访问问题:TLB(Translation Lookside Buffer)
-
大内存页表问题:
-
多级页表
-
反转页表
-
页表总大小的计算:
-
页表总大小 = 页数(2页数位) × 每个页表项(PTE)的大小(物理地址【不含偏移量】位数 + 状态位【Modified等】)
2.5 转译后备缓冲区(Translation Lookside Buffer)
每个虚拟地址引用需要两个物理内存引用:
-
1:到页表获取映射
-
2:到页帧本身
将最近的映射缓存到转移后备缓冲区(TLB):
-
利用大多数程序经常引用少量页面的事实
-
每个TLB表项都是一个页表项(与页表项(PTE)具有相同的元素)
-
TLB中大约由64-256个PTE
工作原理:
-
如果想MMU提供一个虚拟地址,硬件通过并行比较所有条目检查TLB
-
如果匹配有效,则从TLB中获取该页,而不遍历页表
-
如果匹配无效:
-
MMU检测遗漏并执行普通页表查找
-
然后它从TLB中驱逐一个页面并用新的条目替换它,以便下次在TLB中找到该页
-
2.6 多级页表(Multi-level Page)
Two-Levle Page:
3.页替换(Page Replacement)
3.1 分页策略(Paging Policies)
取策略(Fetch Policy):什么时候应该把一个page从secondary strorage(disk) fetch到main memory
放置策略(Placement Policy):当page被放入main memory中,它应该放哪里
-
确定进程块在实际内存中的位置
-
对于pure segmentation系统:First-fit, next-fit, ...
-
对于paging(and segmentaion with paging):硬件决定放置page的位置;因为所有内存帧都是等效的,所以选择与帧位置无关
替换策略(Replacement Policy):当前的main memory中,有哪个page要被删除,如果main memory没有足够的空间当导入其他page时
基础页替换步骤:
-
查找页面在磁盘上的位置
-
找到一个空间的页面帧
-
如果有一个空闲的页面帧就使用它
-
否则,使用页面替换算法选择一个victim frame
-
将选定的页写入磁盘,并更新任何必要的表
-
-
从磁盘中读取请求的页面
-
重新启动用户进程
3.2 页替换算法
3.2.1 概述
主要目标:
-
实现低页错误率
-
页错误率:是指当前程序访问在不同内存中的页面时发生页错误的概率。降低页错误率时页面置换算法的主要目标,因为页错误会导致性能显著下降
-
-
保证频繁使用的页面保留在内存中
-
页面置换算法应确保那些频繁使用的页面尽可能长时间留在内存中,这样可以减少页错误,因为这些页面在短时间内可能再次被访问
-
-
被替换的页面在短期内不再需要
-
理想情况下,被选为victim frame的页面应该时短期内不再需要访问的页面,这样可以降低未来的页错误率
-
-
减少页错误的延迟
-
当发生页错误时,系统需要从磁盘加载所需的页面,这个过程会导致延迟,减少这种延迟页也是页面置换算法的目标之一
-
-
高效算法的实现
-
替换那些不需要写回的页面
-
有些页面在被替换时需要写回磁盘,而有些页面则不需要写回(如只读页面),优先替换那些不需要写回的页面,可以减少页错误的时间和开销
-
引用字符串(reference string):
-
定义:是被引用的页面的序列,在内存管理和页面置换算法中,引用字符串用来表示程序访问内存页面的顺序
-
示例:假设有以下一系列地址访问:123,215,600,1234,76,96
-
地址转换位页面号:页面号=内存地址/页面大小
-
根据计算,引用字符串为:1,2,6,12,0,0
-
使用引用字符串评估页面置换算法:
-
运行页面置换算法:
-
将算法应用到一个特定的引用字符串上
-
引用字符串表示内存访问的顺序,即页面被访问的序列
-
-
计算页面错误数:
-
记录算法在处理该引用字符串时发生的页面错误次数
-
页面错误发生在需要访问的页面不存在的内存中时,操作系统必须将该页面从磁盘加载到内存中
-
页面置换算法的数据结构:
-
位图:
-
位图是一种紧凑的表示方法,其中每一位代表一个页面的状态
-
位图通常用于快速查找和更新页面状态
-
-
链表:
-
链表是一种动态数据结构,每一个几点表示一个页面, 结点之间通过指针链接
-
链表可以用于维护页面的舒徐,方便实现先进先出和最近最少使用(LRU)等算法
-
链表可以是单向链表或双向链表,根据具体需求选择
-
3.2.2 最佳置换算法(OPT)
是一种理论上的理想算法,它选择在未来最长时间内不会访问的页面作为victim frame。这种算法需要预知未来的页面访问顺序,因此在实际中难以实现,主要用于比较其他算法的效果
3.2.3 FIFO
概述:FIFO算法选择最早进入内存的页面作为victim frame,它使用一个队列来跟踪页面记载的顺序,队列最前面的页面就是最早加载的页面
为什么FIFO可能是好的:
-
假设长期未被使用的页面不再需要:FIFO的基本假设是,页面驻留在内存中的事件越长,它被再次访问的可能性就越小,因此,最早分配的页面可能不再被使用,替换它是合理的
为什么FIFO可能不太好:
-
不考虑局部性原理:FIFO不考虑程序的局部性原理,即程序倾向于访问最近使用的页面,因此,FIFO可能会替换掉那些实际上仍然频繁使用的页面,导致较高的页面错误率。
-
受Belady's Anomaly影响:Belady's Anomaly是指在某些情况下,增加物理内存的大小反而会导致页面错误率的上升。FIFO是一种可能会出现Belady's Anomaly的算法,这意味着在某些情况下,增加内存并不能改善性能,反而会使情况变得更糟
Belady's Anomaly概念:是指在某些页面置换算法中,增加物理内存的页面帧数量并不总是会减少页面错误次数,反而在某些情况下会增加页面错误次数。
-
正常情况:增加页面帧的数量会减少页面错误次数。
-
异常情况:增加页面帧数量反而会导致页面错误次数增加
3.2.4 Second Chance
是FIFO算法的改进,通过在页面表条目中使用一个引用为(referenced bit)来改进FIFO算法的性能
引用位的使用:
-
R=0,初始状态:当页面首次被加载到内存中,引用位R被设置为0
-
R=1,当页面被引用时:放页面被访问(读取或写入)时,引用位R被设置为1
选择要替换的vcitim frame:
-
选择FIFO队列的头部页面:按照FIFO算法的思路,首先选择队列头部的页面作为候选的victim frame
-
检查引用位:
-
如果引用位为1:
-
将引用位重置为0
-
将该页面的到达时间重置为当前时间
-
将该页面重新放置到队列的末尾
-
-
如果引用位为0:
-
选择该页面作为victim frame,将其移出内存
-
-
-
继续处理下一个页面:重复上述步骤,直到找到引用位为0的页面并将其移出内存
3.2.5 Clock算法
Clock算法是对Second Chance 的一种改进版本。它将页面组织成一个环形,并使用一个指针循环遍历页面。每个页面有一个引用位(reference bit),当页面被访问时,引用位设置为1。当需要选择victim frame时,指针找到引用位为0的页面,选择它作为victim frame;如果遇到引用位为1的页面,将其引用位为重置为0,并继续寻找。
为什么Clock算法比second chance好:
-
高效的实现:Clock算法使用环形缓冲区和循环指针,比队列实现更简单和高效
-
减少处理开销:每次只需检查当前指针指向的页面并移动指针,并不需要频繁地将页面重新插入队列的末尾
-
局部性处理:Clock算法依然保留了Second Chance的优点,即通过引用位避免频繁访问的页面被替换
3.2.6 NRU算法
NRU(Not Recently Used)算法基于两个位(参考位R和修改位M)来选择victim frame。参考位表示页面最近是否被访问过,修改位表示页面是否被修改过。NRU优先选择那些既没有被最近访问过也没有被修改过的页面。
工作原理:
-
页面状态管理:
-
Class 0:R=0,M=0 - 页面既没有被引用也没有被修改
-
Class 1:R=0,M=1 - 页面没有被引用但被修改了
-
Class 2;R=1,M=0 - 页面被引用了但没有被修改
-
Class 3:R=1,M=1 - 页面被引用并且被修改了
-
-
页面替换策略:
-
优先级:NRU算法选择Class 0进行替换,如果没有,依次选择Class1,Class 2,最后是Class3
-
-
周期性重置:为了确保参考位和修改位能够反映页面的最近使用情况,操作系统会定期重置所有页面的引用位,这样可以确保即使页面之前被引用过,如果在最近一段时间内没有再次被引用,其引用位会变为0
为什么NRU算法比Second Chance好:
-
更多信息:NRU算法利用两个位提供更多关于页面使用情况的信息,而不是只依赖一个引用位
-
分类替换:通过将页面分类闭并根据优先级替换,NRU算法能够更有效地减少页面错误,尤其是在内存资源优先的情况下
-
灵活性:NRU算法可以更灵活地处理页面的替换,特别是考虑页面是否被修改,这在数据一致性和写回策略具有重要意义
3.2.7 LRU算法
LRU(Least Recently Used)算法选择最久未被使用的页面作为victim frame,它假设最近使用过的页面在不久的将来仍然会被使用,因此选择最近最少使用的页面进行替换
基本原理:
-
关联时间:LRU算法为每个页面关联了该页面的最后一次使用时间
-
局部性原理:LRU算法基于“局部性原理”,即一个页面在过去被使用的可能性很大,未来也很可能再次被使用
-
替换策略:LRU选择最近未被使用时间最长的页面进行替换
使用过去行为预测未来行为:
-
记录页面引用时间:LRU算法通过记录页面的最后一次使用时间来跟踪页面的使用情况
-
利用过去行为预测未来行为:通过观察页面的使用情况,LRU算法尝试预测哪些页面将在未来被使用,以便更好地决定哪些页面应该被保留在内存中,哪些页面应该被替换
3.2.8 NFU算法
定义:Not Frequently Used基于软件计数器的页面置换算法,它尝试模拟最近最少使用(LRU)算法的行为。NFU算法在每个时钟中断时检查每个页面,并根据引用位和软件计数器对页面进行评估,选择最不经常使用的页面进行替换
基本原理:
-
页面计数器:NFU算法为每个页面关联了一个软件计数器,该计数器初始值为0
-
时钟中断:在每个时钟中断时,操作系统检查每个页面的引用位,并将其引用位的值添加到该页面的计数器中,如果引用位被设置为1,则同时清楚该位
-
页面替换:
-
如果页面的引用位为1,则将引用位的值添加到页面的计数器中,并清除该位
-
如果页面的引用位为0,则仅将引用位的值添加到页面的计数器中
-
最终选择具有最低计数器值的页面进行替换
-
问题:
-
计数器过大的页面:有些页面可能被频繁地使用,导致其计数器值很大
-
程序行为变化:如果程序的行为发生变化,某些页面可能不再被使用,但NFU算法不会忘记他们,仍然根据计数器值进行替换
与LRU算法的区别:
-
更新频率:LRU算法在每次指令执行后更新,因此其解析度很高,而NFU算法在时钟中断后更新,因此其解析度较低
-
计数器权重;在给定时钟中断期间,NFU算法中的计数器给出相同的权重,无论页面被引用了多少次,这与LRU算法不同,后者根据页面的实际使用情况进行替换决策
3.2.9 Aging算法
是一种改进的不经常使用算法(NFU)
定义:通过在每个时钟中断时对页面计数器进行右移操作(即除以2),并将引用位添加到计数器的最左侧(最高有效位),来实现对页面使用频率的评估和对页面使用时间的考虑
基本原理:
-
基于NFU算法:老化算法基于NFU算法,使用页面计数器来评估页面的使用频率
-
时钟中断处理:
-
右移操作:将每个页面的计数器值右移一位,实现对页面使用频率的降低
-
添加引用位:将每个页面的引用位添加到计数器的最左侧,以考虑页面的最近使用情况
-
-
页面替换:选择最低计数器值的页面进行替换
更重视最近引用:
-
权重调整:通过在计数器中添加引用位并在右移操作后保留其值,使得最近被引用的页面在计数器中占据更高的位数,从而给予最近引用更多的权重
-
反映时间因素:由于计数器会随时间的推移而逐渐减小,被最近引用的页面的计数器值会相对较高,因此在页面替换时更有可能被保留在内存中
与LRU的区别:
-
时间粒度:LRU算法是在每次内存访问后更新页面的状态,而老化算法时根据时钟中断来更新页面的计数器状态,因此其时间粒度更粗
3.2.10 LFU算法
LFU算法选择在过去一段时间内被访问次数最少的页面作为victim frame。它维护一个计数器来记录每个页面的访问次数,选择计数器值最小的页面进行替换。
3.3 页面获取策略
页面获取策略,即决定何时将页面加载到主存储器的策略,具体包括了按需分页策略和预分页策略。
按需分页策略:
-
加载时机:页面按需加载,而不是提前加载
-
启动过程:进程启动时,其页面并不会提前加载到内存中,只有在页面缺失时,才会将页面加载到内存中
-
页面缺失处理:当进程访问到未加载到内存中的页面时,会发生页面缺失,此时会将页面从磁盘加载到内存中
预分页策略:
-
加载时机:页面在进程运行之前就加载到内存中
-
需要的工作集:需要一个工作集在上下文切换时加载页面
-
示例:在进程启动之前,系统可能会预加载一些常用的页面到内存中,以减少后续页面缺失的频率
3.4 内存抖动(Thrashing)
内存抖动:发生在一个进程执行时频繁地发生页面调度操作,即将页面从内存中换出到磁盘中,然后再将其换入内存,反复循环,导致进程花费大量时间在页面调度上,而不是在执行实际的计算任务上
原因:
-
页面帧不足:一个进程被分配的页面帧数太少,无法容纳当前的工作集
-
页面调度频繁:由于工作集无法完全放入内存,因此进程频繁地发生页面调度操作,不断将页面从内存中换出,然后再换入
结果:
-
高页面错误率:由于页面帧不足,进程频繁发生页面缺失,导致页面错误率很高
-
低CPU利用率:由于进程花费大量时间在页面调度上,实际计算任务的执行时间减少,导致CPU利用率低
-
多道程序设计增加:操作系统可能错误地认为系统需要增加多道程序设计度,从而添加更多进程到系统中,加剧了内存抖动的问题
解决方案:
-
工作集
-
工作集定义:一个进程的工作集是当前需要的页面集合,即其正在活动中的页面
-
提供足够的页面帧:该方法的核心思想是为每个进程提供足够数量的页面帧,以满足其当前的工作集需求
-
工作集的管理:通过跟踪进程的工作集,操作系统可以动态地调整为进程分配的页面帧数量,以确保进程能够高效地运行而不发生内存抖动
-
-
页面错误帧率:
-
工作集的概念:同样基于工作集的思想,Denning提出了一个进程和的工作集是当前需要的页面集合
-
示例:假设一个进程的工作集是由页面A、B、C和D组成,而当前只有页面A和B被加载到内存中,而C和D没有。在这种情况下,页面A和B是进程当前需要的页面,因此它们的页面错误帧率会相对较低。
-
-
工作集的重要性:不要运行一个进程,如果其工作集无法全部保持在内存中
3.5 工作集模型(Working Set Model)
定义:是指最近k次内存引用中使用的页面集合,这个k值可以根据需要进行调整,通常用来表示进程在一段时间内活跃的页面数量
工作集大小的函数:
-
w(k, t)函数:这个函数表示在时间t时工作集的大小。它是一个关于k的单调非递减函数,即随着k值的增加,工作集的大小不会减小
-
有限的极限:w(k, t)函数的极限是有限的,这意味着随着时间的推移,工作集的大小会趋于一个稳定的值,而不会无线增长
工作集的变化过程:
-
启动阶段:当一个进程启动时,其工作集会随着时间的推移而增长
-
有限的极限:w(k, t)函数的极限是有限的,这意味着随着时间的推移,工作集的大小会趋于一个稳定的值,而不会无线增长
-
过渡期:当进程从一个局部性转移到另一个局部性时,工作集会再次增长。这是因为进程开始访问新的页面集合,导致工作集的大小再次增加
-
稳定期:最终,工作集会在新的局部性中达到稳定的状态,并逐渐减小直至稳定。在这个阶段,进程更倾向于访问新的局部性中的页面,而不是之前的页面集合
3.5.1 工作集算法
基本思想:
-
页面错误处理:当发生页面错误时,查找一个不在工作集中的页面,并将其替换出去
-
工作集定义:工作集是指在最近的k次内存引用中使用的页面集合
近似方法:
-
基于执行时间:不再根据k次内存引用中使用的页面集合,而是根据执行时间
-
当前虚拟时间:进程实际使用的CPU时间称为其当前虚拟时间
-
工作集定义:进程的工作集是其在过去τ秒的虚拟时间内访问的页面集合
实践中的WS算法:
-
页表项的扩展:为页表项添加一个"最后使用时间"字段
-
时钟周期处理:在每个时钟周期(每m毫秒)清除所有页面的引用位,并记录进程的虚拟时间t
-
寻找替换页面:扫描进程在物理内存中的所有页面:
-
如果页面的引用位为1,更新页面不在工作集中,将其替换出去
-
如果页面的引用位位0:
-
如果(t-LTU)大于 τ,说明该页面不在工作集中,将其替换出去
-
如果(t-LTU)小于等于τ,则记录年龄最大的页面,即age=当前虚拟时间-最后使用时间
-
-
如果在扫描整个页表时找不到可替换的页面:
-
如果存在R=0的页面:
-
如果在扫描过程中找到了一个或多个引用位为0的页面,即R=0,那么会选择其中年龄最大的页面进行替换
-
这是因为这些页面很可能不在工作集中,因此可以被替换出去
-
-
所有页面的引用位均为1:
-
如果在扫描过程中没有找到引用位为0的页面,即所有页面的引用位都为1,则表示没有页面不在工作集中
-
在这种情况下,将选择一个页面进行替换,通常是选择一个干净的页面(即未被修改过的页面)
-
选择干净的页面进行替换的原因:它们不需要写回到磁盘,替换过程会更加高效
-
3.5.2 WSClock算法
概述:
-
基于时钟算法:WsClock算法是基于时钟算法的改进版本
-
使用工作集信息:与前面的工作集算法类似,WSClock算法也使用工作及信息来进行页面置换决策
-
环形页面列表:使用一个环形列表来管理页面帧
算法步骤:
-
页面添加:所有页面被放置在一个环形列表中。当页面被添加时,它们会被插入到环中
-
时钟手指移动:时钟手指在环形列表上移动,每次页面错误时会被激活
-
页面置换决策:
-
如果页面的引用位R=1,表示页面被访问过,将引用位R设为0并更新页面的最后使用时间,然后继续寻找下一个页面
-
如果页面的引用位R=0且修改位M=1,表示页面是脏页,需要将其写回磁盘并清除修改位M
-
如果页面的引用位R=0且页面的年龄小于等于τ(工作集阈值),则继续寻找下一个页面
-
如果页面的引用位R=0且页面的年龄大于τ,且页面是干净页,则选择该页面进行替换
-
当时钟手指(clock hand)绕到环形列表的起始点时会发生的情况:
-
情况一:至少有一个页面已经被安排了写回操作
-
页面已被修改:在时钟手指回到起始点时,至少有一个页面已经被修改过,需要被写回到磁盘
-
干净页面置换:找到第一个干净页面,并将其置换出去。
-
-
情况二:没有页面被安排写回操作(所有页面都在工作集中)
-
使用干净页面:如果没有页面被修改过(即所有页面都是干净页面),则可以选择其中的任意一个干净页面进行置换
-
无干净页面:如果不存在干净页面,则选择当前页面,并将其写回到磁盘
-
4.段(Segmentation)
分段概述:
-
分段是一种支持用户视角的内存管理方案
-
一个段是一个逻辑实体,程序员知道并使用它作为单一的逻辑实体
-
整个逻辑地址空间被视为段的集合,每个段都有一个编号和长度
段的定义:是一个自包含的单元,包含从0到某个最大值的线性地址序列
段的特性:
-
一个段通常不包含不同类型的混合内容
-
每个段的长度可以变化,从0到允许的最大值
-
段的长度在执行过程中可以改变而不会影响其他段
用户视角的程序:
-
用户视角下,程序是段的集合,一个段是一个逻辑单元,例如:主程序、程序过程、函数、符号表、堆栈
4.1 地址转换方案(Address Translation Scheme)
逻辑地址或虚拟地址:
-
虚拟段号(s)
-
偏移量(d)
段表(由操作系统维护):
-
将二维物理地址映射到内存地址
-
虚拟段号用作段表的索引
-
每个表项包含:
-
基址:段在内存中驻留的起始物理地址
-
限长:指定段的长度
-
地址转换过程:
-
当CPU生成一个逻辑地址时,该地址被发送到内存管理单元(MMU)
-
MMU使用逻辑地址中的段号作为段表的索引来查找对应的段表项
-
偏移量和段的限长比较,如果偏移量大于段的限长,则生成无效地址错误
-
否则,将偏移量加到段的基址上,形成物理地址
4.2 优缺点
优点:
-
动态调整段大小:允许段根据需要动态增长或收缩,可以更加有效地利用内存,避免固定大小带来的限制
-
段的保护信息:每个段可以单独设置权限,提供更细粒度的访问控制,增强内存保护机制
-
简化的程序链接:段可以独立存在,使得链接过程简单直观,无需复杂的重定位
-
代码共享:通过共享代码段,多个进程可以访问同一份代码,减少内存的冗余加载,提高内存利用率
缺点:
-
程序员的额外负担:编程时需要考虑内存分段的细节,增加了编程复杂度,尤其是对底层编程带来挑战
-
内存碎片化:随着时间推移,内存中的空闲空间可能会被分割成很多个小块,导致无法有效利用,造成内存浪费
-
大段的管理问题:如果某个段非常大,无法一次性加载到物理内存中,导致需要特殊的内存管理策略,增加了系统的复杂性
4.3 段页式内存管理(Segmentation With Paging)
概述:段页式内存管理结合了段式和页式内存管理的优点,以提供更高效的内存管理方式。这种方式首先将内存划分为若干段,然后再将每个段划分为若干页。
段页式内存管理的工作流程
-
逻辑地址结构:逻辑地址被分为三个部分:段号(Segment Number, SN)、页号(Page Number, PN)、页内偏移量(Page Offset, PO)。
例如,一个逻辑地址可以表示为 (SN, PN, PO)。
-
段表(Segment Table):操作系统维护一个段表,每个段表条目(Segment Table Entry, STE)包含该段的基地址和段长度。基地址指向该段的页表。
-
页表(Page Table):每个段都有自己的页表,页表条目(Page Table Entry, PTE)包含每个页的物理地址。
地址转换过程
-
段表查找:从逻辑地址中提取段号(SN),在段表中查找对应的段表条目(STE),获得该段的基地址和长度。
-
页表查找:使用段表条目中的基地址加上页号(PN)来查找页表条目(PTE),获取对应页的物理地址。
-
计算物理地址:将页表条目中的物理地址与页内偏移量(PO)相加,得到最终的物理地址。
优点
-
灵活性:结合了段的逻辑划分和页的固定大小管理,既可以支持逻辑上的模块化,又能有效利用内存。
-
保护性:段的保护性(如只读、只写等)和页的保护性(如禁止访问)可以共同作用,提供更强的内存保护。
-
内存利用效率:避免了段式管理中的外部碎片和页式管理中的内部碎片,提供了更好的内存利用率。
示例
假设一个逻辑地址 (SN, PN, PO),其中 SN = 2, PN = 3, PO = 1024。
-
段表查找:从段表中查找段号 2 的段表条目(STE),假设段基地址为 4000。
-
页表查找:使用段基地址 4000 加上页号 3,查找页表条目(PTE),假设物理地址为 5000。
-
计算物理地址:将物理地址 5000 加上页内偏移量 1024,得到最终的物理地址 6024。