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

第五章、 虚拟内存

 真题考点

考点一:虚拟内存作为缓存的工具

(1)在任意时刻,虚拟页面的集合都分为三个不相交的子集:

——未分配的:VM 系统还未分配(或者创建)的页。未分配的关联,因此也就不占用任何磁盘空间。缓存的:当前已缓存在物理内存中的已分配页。

——未缓存的:未缓存在物理内存中的已分配页。

(2)页表

        页表(page table)是一个存放在物理内存中的数据结构,页表将虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。

考点二:地址翻译

(1)虚拟地址和物理地址的组成

 

(2)页命中和缺页

        页面命中完全是由硬件来处理的,与之不同的是,处理缺页要求硬件和操作系统内核协作完成,当页面不命中时,CPU 硬件执行的步骤:

  1. 处理器生成一个虚拟地址,并把它传送给 MMU
  2. MMU 生成 PTE 地址,并从高速缓存/主存请求得到它。
  3. 高速缓存/主存向 MMU 返回 PTE
  4. PTE 中的有效位是零,所以 MMU 触发了一次异常,传递 CPU 中的控制到操作系统内核中的缺页异常处理程序。
  5. 缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘。
  6. 缺页处理程序页面调入新的页面,并更新内存中的 PTE
  7. 缺页处理程序返回到原来的进程,再次执行导致缺页的指令。CPU 将引起缺页的虚拟地址重新发送给 MMU

(3)TLB(加速地址翻译)

在 MMU 中包括了一个关于 PTE 的小的缓存,称为翻译后备缓冲器(TLB)

(4)Intel Core i7/Linux 内存系统的相关考点

  1. Ll、L2 和 L3 高速缓存是物理寻址的,块大小为 64 字节。L1 和 L2 是 8 路组相联的,而 L3 是 16 路组相联的。
  2. 页大小可以在启动时被配置为 4KB 或 4MB,Linux 使用的是 4KB 的页,并使用四级页表。
  3. Linux 虚拟内存空间:Linux 为每个进程维护了一个单独的虚拟地址空间。内核虚拟内存包含内核中的代码和数据结构。内核虚拟内存的某些区域被映射到所有进程共享的物理页面。例如,每个进程共享内核的代码和全局数据结构。

考点三:内存映射

(1)内存映射

        Linux 通过将一个虚拟内存区域与一个磁盘上的对象(object)关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射。虚拟内存区域可以映射到两种类型的对象中的一种:

  1. Linux 文件系统中的普通文件
  2. 匿名文件:映射到匿名文件的区域中的页面有时也叫做请求二进制零的页

         无论在哪种情况中 旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换文件之间换来换去。交换文件也叫做交换空间,在任何时刻,交换空间都限制着当前运行着的进程能够分配的虚拟页面的总数。

(2)结合内存映射再看 fork()、execve()

———fork()

        当 fork 函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建虚拟内存,它创建了当前进程的 mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

——execve()

假设运行在当前进程中的程序执行了如下的 execve 调用:

execve('a.out", NULL, NULL);

execve 函数在当前进程中加载并运行包含在可执行目标文件 a.out 中的程序,用 a.out 程序有效地替代了当前程序。加载并运行 a.out 需要以下几个步骤:

1)删除已存在的用户区域。删除当前进程虚拟地址的用户部分中的已存在的区域结构。

2)映射私有区域。为新程序的代码、数据、bss 和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为 a.out 文件中的.text 和.data 区。 bss 区域是请求二进制零的,映射到匿名文件,其大小包含在 a.out 中。栈和堆区域也是请求二进制零的,初始长度为零。

3)映射共享区域。

4)设置程序计数器(PC)。

考点四:动态内存分配

(1)分配器

动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块(block)的集合来维护。每个块就是一个连续的虚拟内存片(chunk),要么是已分配的,要么是空闲的。分配器有两种基本风格。

——显式分配器:要求应用显式地释放任何已分配的块。

——隐式分配器:要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块。隐式分配器也叫做垃圾收集器

(2)碎片

        造成堆利用率很低的主要原因是一种称为碎片的现象,当虽然有未使用的内存但不能用来满足分配请求时,就发生这种现象:

——内部碎片:是在一个已分配块比有效载荷大时发生的。在任意时刻,内部碎片的数量只取决于以前请求的模式和分配器的实现方式。产生的原因有:1)维护数据结构产生的开销;2)增加块大小以满足对齐的约束条件;3)显式的策略决定(比如, 返回一个大块以满足一个小的请求)

——外部碎片:当空闲内存合计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以来处理这个请求时发生的。

(3)空闲块的组织

——隐式空闲链表:通过头部中的大小字段—隐含地连接所有块显式空闲链表:在空闲块中使用指针

——分离的空闲列表:按照大小分类,构成不同大小的空闲链表

——按块按大小排序—平衡树:在每个空闲块中使用一个带指针的平衡树,并使用长度作为权值

①隐式空闲链表:

(a)概念

        在这种情况中,一个块是由一个字的头部、有效载荷,以及可能的一些额外的填充组成的。头部编码了这个块的大小(包括头部和所有的填充),以及这个块是已分配的还是空闲的。头部后面就是应用调用 malloc 时请求的有效载荷。有效载荷后面是一片不使用的填充块,其大小可以是任意的。需要填充有很多原因。比如,填充可能是分配器策略的一部分,用来对付外部碎片。或者也需要用它来满足对齐要求。

(b)放置已分配的块——放置策略

        一些常见的策略是首次适配、下一次适配和最佳适配。

放置策略

描述

比较

首次适配

从头开始搜索空闲链表,选择第一个合适的空闲块

速度(吞吐率):下一次适配 > 首次适配 > 最佳适配

内存利用率:最佳适配 >首次适

配 >下一次适配

下一次适配

是从链表的起始处开始每次搜索

最佳适配

配检査每个空闲块,选择适合所需请求大小的最小

空闲块

②显式的空闲链表

        一种更好的方法是将空闲块组织为某种形式的显式数据结构。例如,堆可以组织成一个双向空闲链表,在每个空闲块中,都包含一个 pred(前驱) 和 siicc(后继)指针。

③分离的空闲链表

维护多个空闲链表,其中每个链表中的块有大致相等的大小。

(a)简单分离存储

使用简单分离存储,每个大小类的空闲链表包含大小相等的块,每个块的大小就是这个大小类中最大元素的大小。

(b)分离适配

分配器维护着一个空闲链表的数组。每个空闲链表是和一个大小类相关联的,并且被组织成某种类型的显式或隐式链表

(c)伙伴系统

伙伴系统(buddy system)是分离适配的一种特例,其中每个大小类都是 2 的幂。

考点五:垃圾收集

        垃圾收集器将内存视为一张有向可达图(reachability graph),该图的节点被分成一组根节点(root node)和一组堆节点(heap node),每个堆节点对应于堆中的一个已分配块。根节点对应于这样一种不在堆中的位置,它们中包含指向堆中的指针。这些位置可以是寄存器、栈里的变量,或者是虚拟内存中读写数据区域内的全局变量。

相关文章:

  • PowerPhotos:拯救你的Mac照片库,告别苹果原生应用的局限
  • 2140. 解决智力问题
  • Java导出excel,表格插入pdf附件,以及实现过程中遇见的坑
  • 【年份数据类型及使用】
  • 大型语言模型思维跟踪研究综述
  • 基于51单片机和8X8点阵屏、独立按键的单人弹球小游戏
  • spring security 过滤器链使用
  • 周末总结(2024/04/05)
  • 前端 vs 后端:技术分工详解——从用户界面到系统逻辑的全解析
  • MyBatis八股文-执行流程、延迟加载、一级与二级缓存
  • 判断HiveQL语句为建表语句的识别函数
  • 在Spring Boot中配置数据库连接
  • 一个使用nginx转发的cgi程序示例
  • BEVFormer v2(CVPR2023)
  • Airflow量化入门系列:第一章 Airflow 基础与量化交易场景
  • K8S学习之基础七十二:Ingress基于Https代理pod
  • 【LLM】MCP(Python):实现 SSE 通信的 Server 和 Client
  • NO.66十六届蓝桥杯备战|基础算法-贪心-区间问题|凌乱的yyy|Rader Installation|Sunscreen|牛栏预定(C++)
  • 一键自动备份:数据安全的双重保障
  • Linux网络:数据链路层以太网