文章目录
- 前言
- 内存管理
- 地址空间与地址生成
- 连续内存分配
- 内存碎片
- 连续分配算法
- 碎片整理
- 非连续内存分配
- 虚拟内存
- 管理虚拟内存地址
- 内存分段
- 内存分页
- 段页式内存管理
- 虚拟内存的覆盖技术
- 虚拟内存的交换技术
- 缺页异常
- 内存页面置换算法
- 局部页面置换算法
- Belady现象
- 全局页面置换算法
- 抖动和负载控制
- 小结
前言
本文主要涉及操作系统的简介、硬件结构、内存管理、进程管理、文件系统、设备管理等内容,可以作为学习操作系统的辅助文本记录。撰写本文的目的主要是针对操作系统整体做一个相对完整的梳理,以便后续回顾之用。
本文是第二篇,讲述操作系统的内存管理相关的基本内容。
第一篇:操作系统(一)基础知识及操作系统启动
内存管理
内存是计算机中十分宝贵的资源,相较于磁盘这种大容量低速度的存储而言,内存资源容量小速度高,需要设计合理的管理方式以求最大效率地使用。
内存管理的四个目标:
- 隔离性:内存管理应确保不同进程之间的内存空间互相隔离,每个进程都有独立的虚拟地址空间,使得进程不能直接访问其他进程的内存数据。这样可以提高系统的安全性和稳定性,防止进程之间的干扰和非法访问。
- 保护性:内存管理需要提供机制来保护进程的内存免受非法访问和损坏。操作系统必须确保每个进程只能访问其所拥有的内存区域,并且防止进程越界访问其他进程或操作系统的关键数据。
- 共享性:内存管理应支持进程之间共享内存的机制。共享内存可以提高进程间通信的效率和灵活性,使得多个进程可以在同一块内存区域上进行数据共享,避免了复制数据的开销。
- 虚拟化:内存管理需要实现虚拟内存的概念,将物理内存抽象为虚拟地址空间,使得每个进程都认为自己独占整个地址空间。通过虚拟内存的技术,可以提供更大的地址空间给每个进程使用,并且可以实现内存的合理分配和动态调整,以满足多任务处理和运行时的需求。
地址空间与地址生成
-
地址空间定义
物理地址空间–硬件支持的地址空间(内存和磁盘内存)。
逻辑地址空间–一个运行的程序所拥有的内存范围(一维线性地址空间)
-
地址生成
Q: 逻辑地址空间是如何生成的?
A: 程序编译之后可以变为汇编程序。比如C程序中的变量名就是逻辑地址,汇编程序转换成.o文件其中是机器语言。linker是可以将多个.o程序转换为可执行程序,loader会将可执行程序载入到内存中。这个逻辑地址的生成基本基本不需要操作系统的参与。
CPU通过MMU查找逻辑地址的物理内存地址,找不到会产生缺页中断,去内存中查找。(具体见后面内存管理的相关内容)
-
地址安全检查
操作系统的地址安全检查是一项重要的机制,用于保护计算机系统免受恶意软件和非法访问的攻击。它主要涉及以下几个方面:
- 内存访问权限:操作系统通过为每个进程或线程分配独立的虚拟内存空间来实现内存隔离。每个进程只能访问其自己的虚拟内存地址空间,无法直接访问其他进程的内存。操作系统使用内存管理单元(MMU)来监控内存访问,并检查每次内存访问是否越界或访问了不被允许的内存地址。
- 堆栈保护:堆和栈是常见的内存区域,用于存储程序执行时所需的数据和临时变量。操作系统可以实施堆栈保护技术,包括如栈溢出检测、缓冲区溢出检测等,防止恶意软件通过溢出攻击修改程序的执行流程或执行恶意代码。其中一种常见的堆栈保护技术是使用"栈保护字"(canary),它是一个特殊的值,会被插入到函数调用过程中,用于检测栈的完整性。
- 访问控制:操作系统通过访问控制机制,如用户权限、访问控制列表(ACL)等,限制对系统资源的访问。这包括文件和目录的读写权限、系统调用的访问权限等。只有经过身份验证和授权的用户或进程才能进行特定的操作,阻止了非法访问和潜在的攻击。
- 输入验证:操作系统会对输入数据进行验证,以防止恶意软件或攻击者传递恶意数据触发漏洞。例如,操作系统会检查用户输入的文件名、网络请求和命令行参数等是否包含特殊字符、超出预期长度或违反规定的格式,从而防止注入攻击等安全威胁。
连续内存分配
内存碎片
随着内存分配和进程使用,有一些空闲内存无法被继续利用使用,称这些空闲内存为内存碎片。
内存碎片分为 外部碎片和 内部碎片。
- 外部碎片: 在分配单元之间的未被使用的且很有可能因为太小不会被分配的碎片
- 内部碎片:分配之后,在分配单元之间没有被使用的内存。
连续分配算法
- 首次适配算法
从地址起始处开始寻找,找到第一个能可以容纳所需内存的空闲块就分配。
优点: 高地址会有大块的空闲分区;
缺点:外部碎片,分配大块时则较慢。
- 最优适配算法
分配与需求差别最小的空闲内存块
优点:避免分割大空闲块的拆分,外部碎片产生的尺寸小,当大部分分配时是小尺寸内存,十分有效;
缺点:产生的外部碎片尺寸很小,导致后续根本无法利用而使用;
- 最差匹配算法
分配与需求差别最大的空闲内存块
优点:避免产生琐碎的外部碎片,分配尺寸为中大型的比较合适;
缺点:拆大块导致后来分配大的分配块,分配不到;
以上为简单的内存分配算法,可以看到他们分别适合不同场景下的内存分配。
碎片整理
是指通过调整进程占用的分区位置来减少或避免分区碎片;
-
碎片紧凑(Compaction):通过移动分配给进程的内存分区,以合并内存碎片;
- 碎片紧凑的条件:所有的应用程序可以进行动态重定位(不然进程映射的地址就不正确了)
-
分区兑换(Swapping in/out):通过抢占并回收处于等待状态进程的分区,以增大可用内存空间;
分区兑换带来更多的问题:将哪一个等待的程序换出那?什么时候执行换入、换出这一操作那?
后续会介绍到。
非连续内存分配
连续分配分配给程序的物理内存必须连续
连续分配既有内碎片又有外碎片。
非连续分配的设计目标:提高内存利用效率和管理灵活性。
非连续内存的优点:
- 更好的内存利用和管理
- 允许共享代码与数据
- 支持动态加载和动态链接
虚拟内存
为什么要有虚拟内存?单片机中没有操作系统,每次都要烧录新程序,并且单片机上只能运行一个程序,这是因为单片机的CPU是直接操作内存的物理地址的。如果另一个程序也操作了某个物理地址,那另一个程序肯定直接崩溃了。
Q: (疑惑:如果限定单片机中程序的可访问物理地址,那么是否可以运行多个程序那?)
A: 应该是可以的,但也要设置轮询,并且在切换执行不同程序时,需要保存和恢复各个程序的上下文信息,包括程序计数器、堆栈指针等。这样才能确保程序在切换执行时能够正确地继续执行。
但是对于很多程序在运行的情况下,自己直接写,无疑大大提高了编程的难度。因此虚拟地址的产生就是解决这个问题的,让我们能够不关心物理地址,甚至不用操心程序的中断和恢复,从而降低编程的难度。
操作系统的虚拟内存正是解决这个问题的,操作系统为每个进程分配独立的虚拟地址,所有进程都不访问物理地址。虚拟地址是如何对应物理地址的,进程完全不需要关心,从而可以多个程序的运行。
- 程序所使用的内存地址叫做 虚拟内存地址(Virtual Memory Address)
- 实际硬件里面的空间地址叫做 物理内存地址(Physical Memory Address)
操作系统引入了虚拟内存,进程持有的虚拟地址会通过 **CPU 芯片中的内存管理单元(MMU)**的映射关系,来转换变成物理地址,然后再通过物理地址访问内存。
Q: 虚拟内存如果没有页命中,就要产生缺页,调用缺页异常处理程序,效率会不会很低?
A: 不会的,因为局部性原理已经告诉我们尽管在整个运行过程中程序引用的不同页面的总数可能超出物理内存总的大小,也会产生一定的缺页。但局部性原则告诉我们在任何时刻,程序将趋向于在一个较小的活动页面(active page)集合上工作,这个集合叫做工作集(working set)或者常驻集合(resident set)。在初始开销,也就是工作集页面调度到内存中之后,接下来对这个工作集的引用将导致命中,而不会产生额外的磁盘流量。因此虚拟内存的技术实际上是十分高效的。
管理虚拟内存地址
主要通过 内存分段和 内存分页来管理虚拟内存地址和物理地址之间的关系
内存分段
程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的形式把这些段分离出来。
内存分段是一种管理虚拟内存地址和物理地址之间映射的方法
分段机制下的虚拟地址主要由两部分组成,分别是段选择因子和段内偏移量
-
段选择子中最重要的是段号,用作段表的索引。段表中所保存的是段的基地址、段的界限和特权等级等。
-
段内偏移量位于0~段的界限之间。如果段内偏移量是合法的,就将段基地址+段内偏移量得到物理地址。
内存分段的方法可以将虚拟地址映射到物理地址中去,然而这样的方法有以下两个问题:
- 内存碎片的产生
- 内存交换效率低
由于每个段的长度不固定,所以多个段未必能恰好使用所有的内存空间,会产生了多个不连续的小物理内存,导致新的程序无法被装载,所以会出现外部内存碎片的问题。内存分段管理可以做到段根据实际需求分配内存,所以有多少需求就分配多大的段,所以不会出现内部内存碎片。
我们可以通过碎片紧凑(内存交换)的技术整理内存,以减少外部碎片。但如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿。这也就是所谓的内存交换效率低的问题。
内存分页
系统将虚拟内存分割为称为 **虚拟页(Virtual Page, VP)**的大小固定的块,每个虚拟页的大小为 P = 2 p P = 2^p P=2p字节。物理内存被分割位物理页(Physical Page, PP) 大小也为P字节(物理页也被称为 页帧(Page frame))
内存分页可以用来解决“外部内存碎片”和“内存交换效率低”的问题。
分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫页(Page)。在 Linux 下,每一页的大小为 4KB
。虚拟地址与物理地址之间通过页表来映射。
而当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。
Q: 内存分页是如何解决“外部内存碎片”的问题? (少出现外部碎片)
A: 内存分页由于内存空间都是预先划分好的,也就不会像内存分段一样,在段与段之间会产生间隙非常小的内存,这正是分段会产生外部内存碎片的原因。而采用了分页,页与页之间是紧密排列的,所以不会有外部碎片。但是,因为内存分页机制分配内存的最小单位是一页,即使程序不足一页大小,我们最少只能分配一个页,所以页内会出现内存浪费,所以针对内存分页机制会有内部内存碎片的现象。
Q: 内存分页是如何解决“内存交换效率低”的问题的?(交换的数据量减小)
A: 如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出(Swap Out)。一旦需要的时候,再加载进来,称为换入(Swap In)。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。
Q: 内存分页机制下,虚拟地址和物理地址是如何映射的?
A: 在分页机制下,虚拟地址分为两部分,页号和页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址。
内存分页步骤:
- 把虚拟内存地址,切分成页号和偏移量;
- 根据页号,从页表里面,查询对应的物理页号;
- 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址。
页式内存的问题:
- 访问一个内存单元需要两次内存访问,第一次访问页表项,第二次访问数据
- 页表大小可能非常大
如何处理?
- 缓存(Caching) – 快表
- 间接访问(Indrection)-- 多级页表
- 间接访问(Indrection) – 多级页表
简单的分页有时候占用空间太大。32位操作系统中虚拟空间地址一共有4GB,假设一个页的大小定为 4KB(2^12),那么4GB的虚拟内存一共有100万页左右(2^20)左右,假如每个页需要4字节存储,那么就需要 4MB
来存储这个页表。而一个进程就有一个自己的虚拟地址空间,100个进程则需要 400MB
来存储,这就很恐怖了。
因此出现了多级页表(Multi-Level Page)来解决这样的问题。将页表(一级页表)分为 1024
个页表(二级页表),每个表(二级页表)中包含 1024
个「页表项」,形成二级分页。每个进程都有 4GB 的虚拟地址空间,而显然对于大多数程序来说,其使用到的空间远未达到 4GB,因为会存在部分对应的页表项都是空的,根本没有分配,对于已分配的页表项,如果存在最近一定时间未访问的页表,在物理内存紧张的情况下,操作系统会将页面换出到硬盘,也就是说不会占用物理内存。如果使用了二级分页,一级页表就可以覆盖整个 4GB 虚拟地址空间,但如果某个一级页表的页表项没有被用到,也就不需要创建这个页表项对应的二级页表了,即可以在需要时才创建二级页表。做个简单的计算,假设只有 20% 的一级页表项被用到了,那么页表占用的内存空间就只有 4KB(一级页表) + 20% * 4MB(二级页表)= 0.804MB
,这对比单级页表的 4MB
是不是一个巨大的节约?
页表一定要覆盖全部虚拟地址空间,不分级的页表就需要有 100 多万个页表项来映射,而二级分页则只需要 1024 个页表项(此时一级页表覆盖到了全部虚拟地址空间,二级页表在需要时创建)。
64 位的系统,两级分页肯定不够了,就变成了四级目录,分别是:
- 全局页目录项 PGD(Page Global Directory);
- 上层页目录项 PUD(Page Upper Directory);
- 中间页目录项 PMD(Page Middle Directory);
- 页表项 PTE(Page Table Entry);
- 缓存(Caching) – 快表
多级页表虽然解决了空间上的问题,但是虚拟地址到物理地址的转换就多了几道转换的工序,这显然就降低了这俩地址转换的速度,也就是带来了时间上的开销。程序是有局部性的,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。
我们就可以利用这一特性,把最常访问的几个页表项存储到访问速度更快的 硬件,于是计算机科学家们,就在 CPU 芯片中,加入了一个专门存放程序最常访问的页表项的 Cache,这个 Cache 就是 TLB(Translation Lookaside Buffer, 翻译后备缓冲器) ,通常称为页表缓存、转址旁路缓存、快表等。
在 CPU 芯片里面,封装了内存管理单元(Memory Management Unit)芯片,它用来完成地址转换和 TLB 的访问与交互。
有了 TLB 后,那么 CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的页表。
TLB 的命中率其实是很高的,因为程序最常访问的页就那么几个。
- 反置页表
不让页表与逻辑地址空间的大小相对应,而是让页表与物理地址空间的大小相对应
页寄存器(Page Registers)
每个帧与一个页寄存器相关联,寄存器内容包括:
- 使用位(Residence bit):此帧是否被进程所占用;
- 占用页号(Occupier): 对应的页号p
- 保护位(Protection bits): 访问方式,可读可写等
段页式内存管理
Q :页式内存管理已经很方便,为什么还要用段页式内存管理?
A: 段式内存管理和页式内存管理各有其优势和适用场景,因此在某些情况下会选择使用段页式内存管理。
段式内存管理的优势在于:
- 逻辑地址空间的划分更符合程序的逻辑结构,能够更好地支持多道程序设计和共享代码段。
- 允许不同长度的段,更适用于非均匀性的内存需求。
- 对程序的地址空间进行保护和共享更加灵活。
而页式内存管理的优势在于:
- 更为灵活和高效地利用物理内存,避免了内存碎片问题。
- 实现了虚拟内存的概念,使得程序不需要全部加载到内存中就可以运行。
- 更容易实现地址转换和内存保护。
当系统需要同时满足对逻辑地址的分段和对物理内存的分页管理时,段页式内存管理结合了两者的优点。因此,在一些对内存管理要求比较复杂的场景下,选择使用段页式内存管理可以更好地满足系统的需求。
总之,段页式内存管理相比于单纯的段式或页式管理,能够更加灵活地满足不同程序的内存管理需求,是一种更为综合的内存管理方式。
段页式内存管理实现的方式:
- 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制;
- 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页;
这样,地址结构就由段号、段内页号和页内位移三部分组成。
虚拟内存的覆盖技术
目标:在较小的可用内存中运行较大的程序
方法:将程序划分为若干功能相对独立的模块;将不会同时执行的模块共享同一块内存区域;
- 常用功能的代码和数据常驻内存;
- 不常用功能放在其他程序模块中,在需要时装入内存中;
- 不存在调用关系的模块可以相互覆盖,共用同一块内存区域。
缺点:
- 增加编程难度;
- 增加执行时间;需要从交换内外存的数据
虚拟内存的交换技术
目标:增加正在运行或需要运行的程序的内存
方法:可将暂时不能运行的程序放到外存中;
交换时机:内存空间不够或有不够的可能性时换出;
交换区大小:存放所有用户进程的所有内存映像的拷贝
程序换入时的重定位:为了程序换出再换入的继续执行,需要采用动态地址映射(动态重定位)动态地址映射的方法。
缺页异常
- 指令执行中需要的指令或数据不在内存中时,会发生缺页中断(缺段),处理器会通知操作系统将相应的页面或段调入内存中去;
- 操作系统将内存中暂时不用的页面或段保存到外存中去。(如果没有这个的话,随着时间的增加,程序所有的东西都会加载到内存中)
虚拟页式存储管理的性能
有效存储访问时间(effective memory access time EAT)
EAT = 访存时间*(1-缺页率p)+缺页异常处理时间*缺页率p
内存页面置换算法
当出现缺页异常,需要调入新页面而内存已满时,置换算法选择被置换的物理页面;
设计目标:
- 尽可能减少页面的调入调出次数;
- 把未来不再访问或近期不再访问的页面调出;
页面锁定(frame locking):
- 描述必须常驻内存的逻辑页面
- 操作系统的关键部分;
- 要求响应速度比较快的代码和数据
以上的实现通过页表中的锁定标志位(lock bit)来实现
局部页面置换算法
置换页面的选择范围仅限于当前进程占用的物理页面内
-
最优页面置换算法(OPT,optimal)
缺页时,计算内存中每个逻辑页面的下一次访问时间。选择 未来最长时间不访问的页面。
这是一种理想化的算法,因为我们无法预测未来最长时间不访问的页面,但可以作为置换算法的性能评价依据,最接近这个算法的结果,说明该算法效果更好。
-
先进先出置换算法(FIFO)
选择在内存中驻留时间最长的页面进行置换。
该算法性能较差,缺页率不一定会减少(Belady现象),该算法很少单独使用。
-
最近最久未使用置换算法(LRU, Least Recently Used)
认为过去最长时间没有被访问的页面,在未来被访问的可能性也很低。
该算法是最优置换算法的一种近似,因为我们不能够对未来情况进行分析,但可以依据过去的信息进行分析。
最大的问题是开销大
实现办法:
系统维护一个按照最近一次访问时间排序的页面链表,首节点位最近使用过的页面,尾节点是最久未使用的页面。
虽然 LRU 在理论上是可以实现的,但代价很高。为了完全实现 LRU,需要在内存中维护一个所有页面的链表,最近最多使用的页面在表头,最近最少使用的页面在表尾。
困难的是,在每次访问内存时都必须要更新「整个链表」。在链表中找到一个页面,删除它,然后把它移动到表头是一个非常费时的操作。
-
时钟页面置换算法(Clock)
思路:针对页面的访问情况进行大致统计
数据结构:
- 在页表项中增加一个访问位,描述页面在过去一段时间内的访问情况
- 各个页面组织形成环形链表;
- 指针指向最老的页面,也即最先调入的页面。
实现:
- 页面装入内存时,访问位初始化为0;
- 访问页面时,访问位置1;
- 缺页时,从指针当前位置顺序检查环形链表
- 如果访问位为0,则说明该页面又老又没人用,直接置换这一页面;
- 如果访问位为1,则置0,并将指针移动到下一个页面,直到找到可以置换的页面。
可以看到时钟页面置换算法既有FIFO的影子,又有LRU的特点,即考虑最近最久未使用。
改进的Clock算法
Clock算法如果遇到有修改的页,还要将修改页的数据写回到内存中,这无疑增大了页面置换的开销。
思路:减少修改页的缺页处理开销
算法:在页面中增加修改位,并在访问位时进行相应修改;
缺页时,修改页面标志位,以跳过有修改过的页面。
-
最不常用算法(Least Frequently USed, LFU)
访问计数,每个页面设置一个访问计数,访问页面时,访问计数+1,缺页时,置换计数最小的页面。
特点:算法开销大
开始时使用频繁,但以后不常使用的页面很难被置换走;
解决办法:计数定期右移,数值就会随着时间推移降低。
Belady现象
现象:采用FIFO算法时,可能出现分配的物理页面数增加,缺页次数反而升高的异常现象。
原因:
-
FIFO算法的置换特征与进程访问内存的动态特征矛盾
-
被它置换出去的页面并不一定是进程近期不会访问的
Q: LRU算法不存在Belady现象,为什么?
A:
- 顺序性保证: LRU算法保证了页面访问的顺序性,即根据页面的最近使用情况进行置换。因为通常情况下程序的局部性原理使得最近被访问的页面很可能在不久的将来再次被访问,所以LRU算法更符合实际的访问模式。
- 置换决策的一致性: 随着内存页框数量的增加,LRU算法的缺页率通常会下降或保持不变,而不会像Belady现象描述的那样出现突然的增加。这是因为增加内存页框数量可以提供更多的空间来容纳更多的页面,从而降低缺页率。
时钟页面置换算法也是如此,不存在Belady现象。
全局页面置换算法
置换页面的选择范围是所有可变数目的物理页面
进程在不同阶段的内存需求是变化的,分配给进程的内存也需要在不同阶段有所变化。全局置换算法需要确定分配给进程的物理页面数。
CPU利用率与并发进程数的关系
-
进程数少时,提高并发进程数,可以提高CPU利用率
-
并发进程导致内存访问增加;
-
并发进程的内存访问会降低访存的局部性特征,因为并发切换,导致之前执行的事情跟之后执行的完全没有关系,这样也就降低了访问内存的局部性特征。
-
局部性特征的下降会导致缺页率的上升和CPU利用率的下降;
-
工作集置换算法
一个进程当前正在使用的逻辑页面集合,可表示为二元函数 W ( t , Δ ) W(t, \Delta) W(t,Δ)
- t t t是当前的执行时刻;
- Δ \Delta Δ称为工作集窗口(Working-set Window),即一个定长的页面访问时间窗口
- W ( t , Δ ) W(t, \Delta) W(t,Δ)是指当前时刻 t t t前的 Δ \Delta Δ时间窗口中的所有访问页面所组成的集合;
- ∣ W ( t , Δ ) ∣ |W(t,\Delta)| ∣W(t,Δ)∣指工作集的大小,即 页面数目
进程开始执行后,随着访问新界面逐步建立较为稳定的工作集;
常驻集:在当前时刻,进程实际驻留在内存当中的页面集合;
工作集和常驻集的关系
- 工作集是进程在运行过程中固有的性质;
- 常驻集取决于系统分配给进程的物理页面数目和页面置换算法
缺页率与常驻集的关系
- 常驻集 ⊇ \supseteq ⊇工作集时,缺页较少;
- 工作集发生剧烈变动(过渡)时,缺页较多;(此时可能进程在切换功能模块)
- 进程常驻集大小达到一定数目后,缺页率也不会明显下降(自己用到的都在内存中,再加其他东西,对该进程的缺页率没有影响)
工作集置换算法:
思路: 换出不在工作集中的页面
窗口大小 τ \tau τ: 当前时刻前 τ \tau τ个内存访问的页引用是工作集, τ \tau τ被称为窗口大小
实现方法:
- 访存链表:维护窗口内的访存页面链表;
- 访问内存时,换出不在工作集中的页面,更新访存链表;
- 缺页时,换入页面;更新访存链表
-
缺页率置换算法(PFF, Page Fault Frequency)
缺页率(page fault rate):缺页平均时间间隔的倒数
影响缺页率的因素:
- 页面置换算法
- 分配给进程的物理页面数目
- 页面大小
- 程序的编写方法
实现:
- 访存时,设置引用位标志
- 缺页时,计算从上次缺页时间
t
l
a
s
t
t_{last}
tlast到现在
t
c
u
r
r
e
n
t
t_{current}
tcurrent的时间间隔
- 如果 t c u r r e n t − t l a s t > T t_{current} - t_{last}>T tcurrent−tlast>T,则置换所有在该时间间隔内没有被引用的页
- 如果 t c u r r e n t − t l a s t ≤ T t_{current}-t_{last}\leq T tcurrent−tlast≤T,则增加缺失页到常驻集中。
- T T T为设定的时间间隔
抖动和负载控制
-
抖动问题(thrashing)
抖动:
- 进程物理页面太少,不能包含工作集;
- 造成大量缺页,因而导致频繁置换;
- 进程运行速度因此变慢;
产生抖动的原因:
- 随着驻留内存的进程数目增加,分配给每个进程的物理页面数不断减少,缺页率不断上升;
- 操作系统需要在进程的并发和缺页率之间达到一个平衡;
-
负载控制
通过调节并发进程数(MPL)来进行系统负载控制,在并发进程数和CPU利用率之间做一个平衡。
但是这个平衡我们很难考察到,因此转而利用另一个指标。
平均间隔时间(MTBF)和缺页异常处理时间(PFST)。
如果
- MTBF > PFST,这说明CPU还能处理过来异常,当前的进程数较为合适;
- MTBF< PFST,说明CPU来不及处理异常,需要降低进程数,从而达到平衡。
小结
本节主要操作系统的内存管理的一些基础知识。
本文参考:
- 《深入理解计算机系统》
- 《操作系统》–清华大学网课
- 小林coding
如果您觉得我写的不错,麻烦给我一个免费的赞!如果内容中有错误,也欢迎向我反馈。