当前位置: 首页 > ops >正文

六:操作系统虚拟内存之缺页中断

深入理解操作系统:缺页中断 (Page Fault) 的处理流程

在上一篇文章中,我们介绍了虚拟内存和按需调页 (Demand Paging) 的概念。虚拟内存为每个进程提供了巨大的、独立的虚拟地址空间,并通过页表 (Page Table) 将虚拟页面 (Virtual Page) 映射到物理内存中的物理帧 (Physical Frame)。按需调页的核心在于:只有当程序试图访问一个尚未加载到物理内存中的页面时,操作系统才会将其从磁盘调入。

那么,当程序访问的页面不在内存中时,具体会发生什么?这就是我们今天要深入探讨的——缺页中断 (Page Fault)

1. 什么是缺页中断?

缺页中断是一种特殊的中断 (Interrupt) 或更准确地说,是陷阱 (Trap)异常 (Exception)。它发生在当程序试图访问一个虚拟地址时,内存管理单元 (MMU) 在该进程的页表中查找对应的页表条目 (Page Table Entry, PTE),发现该页面的存在位 (Present Bit) 为 0。存在位为 0 表示该虚拟页面当前不在物理内存中。

此时,MMU 无法完成虚拟地址到物理地址的翻译,硬件会立即停止当前指令的执行,并产生一个内部中断信号,将控制权交给操作系统内核中预设的缺页中断处理程序 (Page Fault Handler)

可以将其类比为:你去图书馆(物理内存)找一本图书(页面),但发现这本书(页面)不在架上(不在内存)。于是你向图书管理员(操作系统)求助,由图书管理员负责找到这本书(从磁盘调入)并带给你。

2. 缺页中断的处理流程

当缺页中断发生后,操作系统的缺页中断处理程序会接管控制权,执行一系列步骤来解决这个“页面不在内存”的问题,以便程序能够继续执行。这个过程是操作系统内存管理的核心部分之一。

以下是典型的缺页中断处理流程:

  1. 陷阱到操作系统内核 (Trap to the Operating System):

    • 当 MMU 检测到访问的页面的存在位为 0 时,它会生成一个硬件中断(通常称为 Page Fault Exception)。
    • 硬件将导致缺页的虚拟地址以及其他相关信息(如访问类型:读/写)压入进程的栈中,并切换 CPU 的执行模式到内核态。
    • CPU 跳转到操作系统预设的缺页中断处理程序的入口地址。
  2. 保存进程状态 (Save Process State):

    • 操作系统中断处理程序会保存当前正在执行的进程的上下文(寄存器、程序计数器等),以便稍后能够恢复其执行。
  3. 验证虚拟地址和权限 (Validate Virtual Address and Permissions):

    • 操作系统首先检查导致缺页的虚拟地址是否合法。也就是说,这个地址是否属于当前进程的有效地址空间(例如,它是否在一个分配给进程的代码、数据或栈段内)。如果地址无效,说明这是一个非法内存访问(例如,访问了不属于自己的内存区域),这通常会导致一个段错误 (Segmentation Fault),操作系统会向该进程发送一个信号,通常会终止进程。
    • 如果地址合法,操作系统还会检查进程对该页面的访问权限(例如,是试图写入只读页面吗?)。权限不匹配也会导致保护错误并可能终止进程。
    • 如果地址合法且权限匹配,处理程序继续执行。
  4. 确定页面在磁盘上的位置 (Determine Page Location on Disk):

    • 操作系统需要知道这个缺失的页面存储在磁盘的哪个位置。对于程序代码和静态数据,它通常位于程序的可执行文件中。对于堆 (heap) 或栈 (stack) 数据,它可能位于系统的交换空间 (Swap Space)分页文件 (Paging File)。操作系统会查找内部的数据结构(例如,页表的扩展信息或单独的结构)来确定磁盘地址。
  5. 查找空闲物理帧 (Find a Free Physical Frame):

    • 操作系统需要在物理内存中找到一个空闲的物理帧,用于加载所需的页面。
    • 操作系统维护着一个空闲物理帧列表。它会首先尝试从这个列表中获取一个空闲帧。
    • 如果存在空闲帧: 直接分配一个空闲帧给该页面。
  6. 执行页面置换 (Page Replacement - If No Free Frame):

    • 如果没有空闲物理帧: 这是内存紧张的情况。操作系统必须选择一个当前已经在物理内存中的页面作为“牺牲者”,将其从内存中移除,以便为新页面腾出空间。这个过程称为页面置换 (Page Replacement)
    • 操作系统会使用某种页面置换算法(如 LRU - Least Recently Used, FIFO - First-In First-Out, Clock 等)来选择要置换的页面。选择的目标通常是希望置换一个将来最不可能被访问的页面。
    • 处理“脏页” (Handle “Dirty” Pages): 在置换页面之前,操作系统会检查被选中的“牺牲者”页面的页表条目中的修改位/脏位 (Dirty Bit)。如果脏位为 1,表示该页面在加载到内存后被修改过,其内容与磁盘上的原始副本不一致。为了保存这些修改,操作系统必须将该页面写回磁盘(通常是写回交换空间)后再释放其物理帧。这是一个额外的、开销较大的 I/O 操作。
    • 如果脏位为 0,表示页面未被修改,可以直接丢弃内存中的副本,因为磁盘上的副本是最新的。
    • 更新被置换页面的页表条目:将被置换页面的存在位设置为 0,清除其物理帧号信息。
  7. 将所需页面从磁盘读入物理内存 (Read Required Page from Disk to Frame):

    • 操作系统发起一个磁盘 I/O 请求,将第 4 步中确定的页面从磁盘位置读取到第 5 或 6 步中找到的物理帧中。这是一个耗时较长的操作(相对于 CPU 指令执行速度)。在等待磁盘 I/O 完成期间,操作系统通常会将 CPU 分配给其他处于就绪状态的进程执行。
  8. 更新页表 (Update Page Table):

    • 磁盘 I/O 完成后,所需页面已成功加载到物理内存的某个物理帧中。
    • 操作系统更新当前进程的页表,找到导致缺页的虚拟页面对应的页表条目。
    • 将该条目的存在位设置为 1。
    • 将该条目的物理帧号更新为页面实际加载到的物理帧号。
    • 根据程序的访问类型,设置页面的访问权限位(读、写、执行)。
    • 将脏位和访问位清零(初始状态)。
  9. 恢复进程执行 (Restart or Continue Process Execution):

    • 操作系统恢复之前保存的进程状态。
    • 最关键的一步是:操作系统会修改程序计数器,使其指向导致缺页中断的那条指令的开头
    • 进程在用户态下恢复执行。当 CPU 再次尝试执行那条指令时,MMU 会再次进行地址翻译。这一次,由于操作系统已经将所需的页面加载到内存并更新了页表,MMU 查找页表时会发现存在位为 1,能够成功翻译虚拟地址为物理地址,指令得以正常完成。

3. 缺页中断处理的例子

假设一个程序试图访问数组 arr[i]。在编译后的机器码中,这可能对应于一条加载(Load)或存储(Store)指令,其操作数使用虚拟地址 &arr + i * sizeof(element)

  1. CPU 执行指令,计算出要访问的虚拟地址 VA
  2. CPU 将 VA 发送给 MMU 进行翻译。
  3. MMU 查找当前进程的页表,发现 VA 所属的虚拟页面的页表条目中,存在位是 0。
  4. MMU 触发一个缺页中断。
  5. CPU 切换到内核态,跳转到操作系统的缺页中断处理程序。
  6. 操作系统:
    • 保存当前进程的上下文。
    • 检查 VA 是否合法地址,以及访问权限(是读还是写,进程是否有权限)。假设合法。
    • 确定 VA 所在的虚拟页面对应于 arr 数组的某个部分,这个页面在磁盘上的某个位置(比如在程序的原始数据段或交换空间)。
    • 查找物理内存中的空闲帧。假设没有空闲帧,需要进行页面置换。
    • 选择一个页面 P_victim 进行置换(例如,使用了 LRU 算法选了一个很久没用的页面)。
    • 检查 P_victim 的脏位。假设 P_victim 的脏位是 1(它被修改过)。操作系统发起一个 I/O 操作,将 P_victim 的内容写回磁盘的交换空间。
    • 操作系统更新 P_victim 对应的页表条目,将其存在位设为 0。
    • P_victim 占用的物理帧现在变为空闲。操作系统发起第二个 I/O 操作,将导致缺页的那个页面(包含 arr[i])从磁盘读入这个刚刚释放的物理帧。
    • I/O 操作完成。操作系统更新 VA 所在页面的页表条目,设置存在位为 1,记录新的物理帧号。
    • 操作系统恢复进程上下文,并将程序计数器设置回导致缺页中断的那条 Load/Store 指令的开头。
  7. 进程在用户态下恢复执行。CPU 再次执行 Load/Store 指令,尝试访问 VA
  8. MMU 再次翻译 VA。这一次,页表中对应的存在位是 1,MMU 成功翻译出物理地址。
  9. CPU 使用物理地址访问内存,Load/Store 指令成功完成。程序继续正常执行。

整个过程从程序执行的角度来看,除了由于磁盘 I/O 导致的延迟之外,是透明的。程序并不知道它刚刚经历了一次缺页中断和页面加载。

4. 缺页中断的开销

缺页中断处理是一个复杂的、涉及硬件和软件协作的过程,其中最耗时的部分是磁盘 I/O 操作。与 CPU 执行一条指令(纳秒级别)相比,磁盘访问需要数毫秒甚至更长时间,这是几个数量级的差异。

频繁的缺页中断(称为颠簸 (Thrashing))会导致系统大部分时间都在忙于页面调入调出,而用户进程实际执行指令的时间很少,系统性能急剧下降。这是物理内存不足以支持当前进程的工作集(Work Set,即进程在一段时间内频繁访问的页面集合)时常发生的情况。

然而,在大多数正常情况下,由于程序的局部性原理 (Principle of Locality)(包括时间局部性和空间局部性),程序在一段时间内只会访问其工作集中的页面。按需调页能够有效地利用物理内存,只加载这些活跃页面,从而在有限的物理内存下支持更多或更大的程序运行。缺页中断虽然有开销,但其带来的内存利用率和多道程序度(Multiprogramming Degree)的提升通常是值得的。

总结

缺页中断是虚拟内存和按需调页机制中不可避免的一部分。它是一个由 MMU 触发的硬件异常,通知操作系统某个被访问的虚拟页面不在物理内存中。操作系统的缺页中断处理程序负责定位缺失页面、寻找物理内存空间(可能需要置换其他页面)、将页面从磁盘加载到内存,并更新页表,最终让导致缺页的指令重新执行成功。

理解缺页中断的处理流程,对于理解虚拟内存的工作原理、内存性能瓶颈以及操作系统的复杂性至关重要。它是现代操作系统能够高效管理内存、运行大型程序和支持多任务的关键技术之一。


http://www.xdnf.cn/news/7850.html

相关文章:

  • PHP:经典编程语言在当代Web开发中的新活力
  • YOLOv4深度解析:从架构创新到工业落地的目标检测里程碑
  • git工具使用
  • 【VxWorks 实时操作系统(RTOS)】常用函数汇总
  • 期刊采编系统安装升级错误
  • 25_05_19Linux实战篇、第一章_01若依前后端部署之路(后端)
  • SpringBoot-SpringBoot源码解读
  • 自动化软件如何确保高可用性和容错性?
  • git中,给分支打标签
  • 第三章 MCU时钟树配置
  • 直线型绝对值位移传感器:精准测量的科技利刃
  • Linux查 ssh端口号和服务状态
  • 故障率预测:基于LSTM的GPU集群硬件健康监测系统(附Prometheus监控模板)
  • 基于 Redis 实现短信验证码登录功能的完整方案
  • matlab实现混沌扩频DCSK的仿真
  • 从运维告警到业务决策:可观测性正在重新定义企业数据基础设施
  • 8-码蹄集600题基础python篇
  • Web Workers 使用指南
  • 在Windows 上安装 OpenSSH 服务端
  • 【C语言】(10)—指针4
  • 卫星互联网:构建全球无缝通信网络的未来
  • Java---斐波那契那数列
  • 智防火灾,慧控能耗:物联网赋能金融行业电气安全革新
  • 软件设计师考试需背诵知识点
  • 微信小程序AI大模型流式输出实践与总结
  • Power Integrations 汽车电源管理方案:为汽车应用增加系统价值
  • 趣味编程:抽象图(椭圆组成)
  • Windows Docker笔记-扩展
  • AbMole| 蛋白酶抑制剂Cocktail(不含EDTA,100X DMSO储液)
  • Java SE 抽象类和接口(下)