物理和虚拟寻址

主存被组织成一个由 个连续的字节单元组成的数组。每字节有一个唯一的物理地址 PA。直接使用物理地址进行寻址称为物理寻址

在虚拟寻址中,CPU 生成一个虚拟地址来访问主存。这个过程中 CPU 上的内存管理单元 MMU 会将虚拟地址转换为物理地址,并利用存放在主存中的查询表来动态翻译虚拟地址,这个表由操作系统管理。

地址空间

一个非负整数地址的有序集合。如果电子科技中的整数连续,称为线性地址空间 位的机器上有 个地址。

虚拟内存作为缓存

虚拟内存可以视作在磁盘上的数组,内容被缓存在主存中。 虚拟内存被分割为多个虚拟页,每个大小为 字节。 任何时刻虚拟页的集合分为三个不相交的子集:

  • 未分配
  • 缓存在物理内存中
  • 未缓存在物理内存中

页表

判定一个虚拟页是否缓存,存放在哪个物理页。 每个页表条目 PTE 由一个有效位和一个 位地址字段组成。 有效位表明虚拟页是否被缓存。若设置,则地址字段表示物理页中的起始地址。若未设置,则地址字段指向磁盘上的起始位置或空地址。

页命中

访问的虚存数据位于物理内存中

页缺失

访问的虚存数据不位于物理内存中,触发缺页异常。 选择一个牺牲页,将新的虚拟页复制到物理页中,更新页表条目,重新执行指令。

虚拟内存的内存管理

每个进程有独立的私有虚拟地址空间,多个虚拟页可以被映射到同一个物理页上。 简化了链接和加载、代码和数据共享。

  • 简化链接:对于 64 位地址空间,代码段总是从虚拟地址 0x40000 开始
  • 简化加载:只需创建新的进程,并将虚拟页标记为无效的
  • 简化共享:
  • 简化内存分配:分配一个适当个数的虚拟内存页面,并将它们映射到任意的 个物理页上

虚拟内存的内存保护

扩展 PTE,增加使用权限位。 SUP 位表示进程是否在超级用户模式下才能访问,READ 位和 WRITE 位控制对页面的读写访问。 如果违反,触发段错误

地址翻译

基本参数:

  • ,虚拟地址的地址数
  • ,物理地址的地址数
  • ,页面大小 虚拟地址组成部分:
  • VPO:虚拟页偏移量
  • VPN:虚拟页号
  • TLBI:快表索引
  • TLBT:快表标记 物理地址组成:
  • PPO:物理页偏移量
  • PPN:物理页号

页命中

  1. 处理器生成虚拟地址,传送给 MMU
  2. MMU 生成 PTE 地址,从主存中得到它
  3. 主存返回 PTE
  4. MMU 构造物理地址,传送给主存
  5. 主存返回请求的数据

页缺失

  1. 处理器生成虚拟地址,传送给 MMU
  2. MMU 生成 PTE 地址,从主存中得到它
  3. 主存返回 PTE
  4. 触发缺页异常
  5. 缺页处理程序确定牺牲页,若其被修改过,先存储到磁盘
  6. 调入新的页面,更新 PTE
  7. 重新执行导致缺页的指令

TLB 快表

关于 PTE 的缓存。 若 TLB 命中,则 MMU 可以直接从 TLB 取出 PLE,减少了一次内存访问。 若 TLB 有 个组, 项(称为 路组相连),则 TLB 索引 TLBI 是由虚拟页号 VPN 的低 位构成的,VPN 的剩余位(高 位)构成 TLB 标记 TLBT。 查找时,先取出 VPN 的低 位,再到对应快表中查找是否存在相同的标记位,若有则直接返回。

动态内存分配

动态内存分配器维护一个称为的进程虚拟内存区。 分配器将堆视为一组不同大小的块,它们要么是已分配的,要么是空闲的。

分配器类型:

  • 显式分配器:要求应用显式地释放已分配的块,如 C 和 C++。
  • 隐式分配器:要求分配器检测一个已分配的块何时不再被程序使用。隐式分配器也叫做垃圾收集器。如 JAVA

分配器的要求

  • 处理任意请求序列
  • 立即响应
  • 只使用堆
  • 满足对齐要求
  • 只能使用空闲的块
  • 不能修改已经分配的块

分配器的目标

  • 最大化吞吐率,即单位时间内完成的请求数
  • 最大化内存利用率,即峰值利用率,设总负载为 ,堆的大小为 ,峰值利用率为

实现问题

  • 如何跟踪空闲块?
  • 如何选择一个合适的空闲块来放置一个新分配的块?
  • 如何处理分配后的空闲块的剩余部分?
  • 如何处理被释放的块?

隐式空闲链表

对于每个块,需要记录其大小和状态。 可以给块增加一个双字对齐的条件,块的大小总是 8 的倍数,则块大小的低 3 位总是 0,可以用来编码状态信息。

放置

要选择用于分配的内存块,需要搜索整个链表,时间复杂度与总块数成线性关系。

  • 首次适配
  • 下一次适配
  • 最佳适配

分割

分配空间小于空闲空间,将该块分割为分配块和一份新的空闲块。

合并

释放已分配块时,可能有其他空闲块与新释放的块相邻,此时可以选择将这两个相邻的空闲块合并。

  • 立即合并
  • 推迟合并 对于向后的合并,可以直接检查下一块的分配状态。 如何实现向前的合并? 可以给每个块添加一个脚部(头部的副本) 事实上,只有空闲块需要脚部,对于已分配的块,可以在头部中空余的低位中标记前一块是否空闲。