1.虚拟内存

虚拟内存: 现代系统提供对主存的抽象概念。虚拟内存是计算机系统内存管理的一种技术,它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换

  • 为每个进程提供一个大的,一致的和 私有的地址空间
  1. 将主存看成磁盘地址空间的高速缓存
  2. 保护了每个进程的地址空间不被其他进程破坏
  3. 为每个进程提供一致的地址空间,简化存储器管理
  • 物理地址: 计算机系统的主存被组织为M个连续的字节大小的单元组成的数组,每个字节的地址叫物理地址.
  1. CPU访问存储器的最自然的方式使用物理地址,这种方式称为物理寻址。
  • 现代处理器使用的是虚拟寻址的寻址形式

  • CPU通过生成一个虚拟地址来访问主存,将虚拟地址转换为物理地址叫做地址翻译 ,地址翻译需要CPU硬件和操作系统之间的紧密结合

  1. CPU芯片上有叫做存储器管理单元的专用硬件。
  2. 利用存储在主存中的查询表来动态翻译虚拟地址。
  3. 查询表由操作系统管理。
  • 地址空间是一个非负整数地址的有序集合,它区分了数据对象(字节)和它们的属性(地址)
  1. N=2^n个地址的虚拟地址空间叫做n位地址空间
  2. 如今操作系统支持32位或64位
  • 一个系统还有物理地址空间,它与系统中物理存储器的M=2^m(假设为2的幂)个字节相对应,一般来说
  1. 字节有一个在虚拟地址空间的虚拟地址
  2. 还有一个在物理地址空间的 物理地址
  3. 两个地址都能访问到这个字节
  • 虚拟内存被组织为一个存放在磁盘上的N个连续字节大小的单元组成的数组,每个字节都有一个唯一的虚拟地址,这个虚拟地址作为到数组的索引,磁盘上数组的内容被缓存到主存中(在某些情况下)

2.虚拟页

  • 虚拟页: 磁盘上的数据被分割称块,这些块是磁盘和主存之间的传输单元,每个虚拟页大小为P=2^p字节。任何时候,虚拟页的集合都被分为3个不相交的子集
  1. 未分配的:VM系统还未分配(或者创建)的页。未分配的块没有任何数据与之相关联,不占用磁盘空间,通过malloc来分配
  2. 缓存的:当前缓存在物理存储器的已分配页
  3. 未缓存的:没有缓存在物理页面存储器中的已分配页
  • 物理内存被分割为物理页,大小也为P字节,被称为页帧

  • DRAM表示虚拟存储器系统的缓存,在主存中缓存虚拟页,有两个特点。

  1. DRAM缓存不命中处罚十分严重,因为磁盘比DRAM慢100000多倍。
  2. 访问一字节开销大,因为从一个磁盘的一个扇区读取第一个字节的时间开销要比从该扇区中读连续的字节慢大约100000倍
  • DRAM缓存的组织结构特点
  1. 虚拟页往往很大
  2. DRAM缓存是全相联,即任何虚拟页都能放在任何物理页中
  3. 精密的替换算法
  4. DRAM缓存总是写回

3.页表

  • 判断命中和替换又多种软硬件联合提供,包括操作系统软件,MMU中的地址翻译硬件页表
  1. 页表是存放在物理存储器的数据结构,页表将虚拟页映射到物理页
  2. 地址翻译硬件将虚拟地址转换为物理地址都会读取页表
  3. 操作系统负责维护页表的内容,以及磁盘及DRAM之间来回传送页
  • 页表就是一个页表条目(PTE)的数组,每个PTE有一个有效位和n位地址字段,虚拟地址空间 中每个页 在页表的固定偏移量处都有一个PTE
  1. 有效位表明虚拟页是否被缓存,如果有效位存在,那么地址字段指向对应的物理存储器。
  2. 如果有效位不存在,地址字段要么为NULL,要么指向虚拟页在磁盘所在的位置

PageTable

  • 页命中: 一个虚拟地址转换为物理地址的过程
  • 缺页: DRAM缓存不命中,处理如下
  1. 读取虚拟地址所指向的PT
  2. 读取PTE有效位,发现未被缓存,触发缺页异常
  3. 调用缺页异常处理程序
  4. 中断结束,重新执行最开始的指令
  5. 在DRAM中读取成功
  • 缺页异常处理程序
  1. 选择牺牲页
  2. 如果牺牲页发生了改变,将其拷贝回磁盘(因为是写回)
  3. 需要读取的页代替了牺牲页的位置
  4. 结果:牺牲页不被缓存,需要读取的页被缓存
  • 操作系统为每个进程提供一个独立的页表,虚拟内存简化了链接、加载、共享以及应用程序的存储器分配

(1)简化链接

  1. 独立的空间地址意味着每个进程的存储器映像使用相同的格式
  2. 文本节总是从0x08048000(32位)处或0x400000(64位)处开始,然后是数据,bss节,栈
  3. 一致性极大简化了链接器的设计和实现

(2)简化加载

  1. 加载器可以从不实际拷贝任何数据从磁盘到存储器
  2. 基本都是虚拟存储系统完成

(3) 简化共享

  1. 独立地址空间为操作系统提供了一个管理用户进程和操作系统自身之间的一致共享机制,例如操作相同的操作系统内核代码,C标准库的printf
  2. 操作系统需要将不同进程的适当的虚拟页映射到相同的物理页面
  3. 多个进程共享这部分代码的一个拷贝,而不是每个进程都要加载单独的内核和C标准库的拷贝

(4)简化存储器分配.

  1. 虚拟页连续(虚拟页还是单独的)
  2. 物理页可以不连续,使得分配更加容易
  • 虚拟内存保护存储器
  1. 在页表条目上添加一些格外的许可位来控制访问
  2. 如果指令违反了许可条件,触发一般保护性异常,然后交给异常处理程序,一般会报告为段错误

4.地址翻译

  • 地址翻译的硬件实现
  1. 页表基址寄存器(PTBR)指向当前页表
  2. n位的虚拟地址包含两个部分

    (1) 一个p位的虚拟页面偏移(VPO)

    (2)一个n-p位的虚拟页号(VPN)

  1. MMU利用VPN选取适当的页面条目(PTE)
  2. 页面条目 (PTE)中物理页号(PPN)和虚拟地址中的VPO串联起来,即是物理地址
  • 利用TLB加速
  1. 在MMU中包含了一个关于PTE的小缓存,称为翻译后备缓冲器(TLB)
  2. TLB是一个小的,虚拟寻址的缓存
  3. 每一行都保存着一个由单个PTE组成的块
  4. TLB通常用于高度的相连性
  • 多级页表

MultiPageTable

多级页表用于解决在很少使用时有一个很大的页表常驻于内存,从两个方面减少了存储器要求

  1. 如果一级页表PTE为空,那么相应的二级页表就根本不会存在—————一种巨大的潜在节约,大部分时候内存都是未分配的
  2. 只有一级页表才需要总是在主存中————虚拟存储器系统可以在需要时创建,页面调入,调出二级页面,减少主存压力

5.虚拟内存结构

VirtualMemory

  • 内核虚拟内存包含
  1. 内核中的代码和数据
  2. 内核虚拟存储器的某些区域被映射到所有进程共享的物理页面,如:内核代码,全局数据结构
  3. 内核虚拟存储器包含每个进程不相同的数据,如页表,内核在进程上下文中时使用的栈,等等
  • Linux缺页异常处理

(1)虚拟地址A是合法的吗,即A在某个区域结构定义的区域内吗?

  1. 缺页处理程序搜索区域结构链表
  2. 把A和每个区域的vm_start和vm_end做比较,通过某种树的数据结构算法查找
  3. 如果不合法,触发段错误。

(2)试图访问的存储器是否合法,即是否有读,写,执行这个页面的权限?

  1. 如果不合法,触发保护异常,终止进程。
  2. 一切正常的话,选择牺牲页,替换,重新执行指令

6.内存映射

  • 内存映射: Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容

  • 一个对象被映射到虚拟存储器的一个区域,可分为 共享对象私有对象

  • 共享对象

  1. 一个进程将一个共享对象映射到它的虚拟地址空间的一个区域
  2. 进程对这个区域的写操作,对于那些也把这个共享对象映射它的虚拟存储器的进程是可见的
  3. 这些变化也会反映到磁盘上的原始对象
  4. 映射到的虚拟存储器那个区域叫做共享区域

SharedObject

  • 共享对象优点
  1. 进程1将共享对象映射到虚拟存储器中,然后虚拟存储器将这一段找一块物理存储器存储
  2. 当进程2也要引用同样的共享对象时,内核迅速判定,进程1已经映射了这个对象
  3. 使进程2的虚拟存储器直接指向了那一块进程1指向的物理存储器
  4. 即使对象被映射到多个共享区域,物理存储器依旧只有一个共享对象的拷贝
  5. 大大解决了物理存储器内存
  • 私有对象
  1. 对一个映射到私有对象的区域做出的改变,对于其他进程不可见
  2. 并且进行的写操作不会反映到磁盘上
  3. 映射到的虚拟存储器那个区域叫做私有区域

PrivitedObject

  • 私有对象的实现——写时拷贝
  1. 私有对象开始生命周期的方式基本与共享对象一样。即使对象被多个引用,在物理内存都只保留一个拷贝
  2. 对于每个映射私有对象的进程,相应私有区域的页表条目都被标记为只读。并且区域结构被标记为私有的写时拷贝
  3. 只要有进程试图写私有区域内的某个页面,那么这个写操作触发保护异常
  4. 故障处理程序会在物理存储器中创建被修改页面的一个新拷贝
  5. 更新页表条目(PTE)指向这个新的拷贝,恢复被修改页面的可写权限
  6. 故障处理程序返回,CPU重新执行这个写操作
  7. 通过延迟私有对象中的拷贝直到最后可能的时刻,写时拷贝充分使用了稀缺的物理存储器

7.动态内存分配

动态内存分配器维护着一个进程的虚拟存储区域,称为 ,堆中只有两种状态

  • 已分配
  1. 已分配的块显式地保留供应用程序使用
  2. 已分配的块保持已分配状态,直到它被释放
  3. 这种释放要么是应用程序显示执行
  4. 要么是存储器分配器自身隐式执行(JAVA)
  • 空闲
  1. 空闲块可用于分配
  2. 空闲快保持空闲,直到显式地被应用分配

存在两种分配器

  • 显式分配器:要求应用程序显式地释放
  1. C语言——malloc和free
  2. C++——new和delete
  • 隐式分配器:要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块,又叫做垃圾收集器
  1. Lisp,ML以及Java等依赖这种分配器