9.虚拟存储器
为了更加有效地管理存储器且少出错,现代系统提供了对主存的抽象概念,叫做虚拟存储器(VM)
。
虚拟存储器
是硬件异常,硬件地址翻译,主存,磁盘文件和内核软件的完美交互。- 为每个进程提供一个大的,一致的和 私有的地址空间。
提供了3个重要能力。
- 将主存看成磁盘地址空间的高速缓存。
- 只保留了活动区域,并根据需要在磁盘和主存间来回传送数据,高效使用主存。
为每个进程提供一致的地址空间
- 简化存储器管理
保护了每个进程的地址空间不被其他进程破坏。
- 将主存看成磁盘地址空间的高速缓存。
程序员为什么要理解它?
虚拟存储器
是中心的。
- 遍布在计算机系统所有层次,硬件异常,汇编器,连接器,加载器,共享对象,文件和进程中扮演重要角色。
虚拟存储器
是强大的。- 可以创建和销毁存储器片(chunk)
- 将存储器片映射到磁盘文件的某个部分。
- 其他进程共享存储器。
- 例子
- 能读写存储器位置来修改磁盘文件内容。
- 加载文件到存储器不需要显式的拷贝。
虚拟存储器
是危险的- 引用变量,间接引用指正,调用
malloc
动态分配程序,就会和虚拟存储器交互。 - 如果使用不当,将遇到复杂危险的与存储器有关的错误。
- 例子
- 一个带有错误指针的程序可以立即崩溃于段错误或者保护错误。
- 运行完成,却不产生正确结果。
- 引用变量,间接引用指正,调用
本章从两个角度分析。
虚拟存储器
如何工作。- 应用程序如何使用和管理
虚拟存储器
。
9.1 物理与虚拟寻址
物理地址(Physical Address,PA)
:计算机系统的主存被组织为M个连续的字节大小的单元组成的数组。每个字节的地址叫物理地址
.CPU访问存储器的最自然的方式使用
物理地址
,这种方式称为物理寻址
。
- 早期的PC,数字信号处理器,嵌入式微控制器以及Cray超级计算机使用物理寻址
。现代处理器使用的是
虚拟寻址(virtual addressing)
的寻址形式。CPU通过生成一个
虚拟地址(Virtual address,VA)
来访问主存。- 将
虚拟地址
转换为物理地址
叫做地址翻译(address translation)
。
- 将
地址翻译
也需要CPU硬件和操作系统之间的紧密结合。- CPU芯片上有叫做
存储器管理单元(Memory Management Unit,MMU)
的专用硬件。
- 利用存储在主存中的查询表来动态翻译虚拟地址。
- 查询表由操作系统管理。
- CPU芯片上有叫做
9.2 地址空间
地址空间(address space)
是一个非负整数地址
的有序集合。
如果地址空间中整数是连续的,我们说它是
线性地址空间(linear address space)
。- 为了简化讨论,我们总是假设使用线性地址空间。
在一个带虚拟存储器的系统中,CPU从一个有
N=2^n
个地址的地址空间
中生成虚拟地址,这个地址空间称为虚拟地址空间(virtual address space)
。一个
地址空间
大小是由表示最大地址所需要的位数来描述的。- 如
N=2^n
个地址的虚拟地址空间叫做n
位地址空间。 - 现在操作系统支持
32位
或64位
。
- 如
一个系统还有
物理地址空间
,它与系统中物理存储器的M=2^m
(假设为2的幂)个字节相对应。
地址空间
的概念很重要,因为它区分了数据对象(字节)和 它们的属性(地址)。
- 每个
字节(数据对象)
一般有多个 独立的地址(属性)
。每个地址都选自不同的地址空间。
- 比如一般来说。
字节
有一个在虚拟地址空间
的虚拟地址
。- 还有一个在
物理地址空间
的物理地址
。 - 两个地址都能访问到这个
字节
。
- 类似现实世界的门牌号, 和经纬度。
- 比如一般来说。
9.3 虚拟存储器作为缓存的工具
感悟
在讲述这一小章之前,必须交代一下我对
虚拟存储器
概念的存疑。
原本我以为虚拟存储器
=虚拟内存
。
以下是虚拟内存
的定义
虚拟内存
是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片
,还有部分暂时存储在外部磁盘存储器
上,在需要时进行数据交换而在下面的定义我们可以看到
CSAPP
中认为虚拟存储器
是存放在磁盘上的。
在此,我们姑且当做两者是不同的东西,以后有更深刻的理解,再思考。
虚拟存储器(VM)
被组织为一个存放在磁盘上的N个连续字节大小的单元组成的数组。
- 每个字节都有一个唯一的
虚拟地址
,这个虚拟地址作为到数组的索引。 磁盘
上数组的内容被缓存到主存
中。- 同存储器层次结构其他缓存一样,
磁盘
上的数据被分割称块
。
- 这些
块
作为磁盘和主存之间的传输单元。 虚拟页(Virtual Page,VP)
就是这个块
- 每个
虚拟页
大小为P=2^p
字节。
- 每个
- 这些
- 物理存储器被分割为
物理页
,大小也为P
字节
- 也被称为
页帧(page frame)
- 也被称为
- 同存储器层次结构其他缓存一样,
任何时候,
虚拟页
的集合都被分为3个不相交的子集。- 未分配的:VM系统还未分配(或者创建)的页。未分配的
块
没有任何数据与之相关联。
- 不占用磁盘空间
- 通过
malloc
来分配
- 缓存的:当前缓存在物理存储器的已分配页。
- 未缓存的:没有缓存在物理页面存储器中的已分配页。
- 未分配的:VM系统还未分配(或者创建)的页。未分配的
9.3.1 DRAM缓存的组织结构
DRAM
表示虚拟存储器系统的缓存,在主存中缓存虚拟页
,有两个特点。
DRAM
缓存不命中处罚十分严重。
- 因为
磁盘
比DRAM
慢100000多倍。
- 因为
- 访问一字节开销
- :从一个磁盘的一个扇区读取第一个字节的时间开销要比从该扇区中读连续的字节慢大约100000倍
DRAM
缓存的组织结构由这种巨大的不命中开销驱动。因此有以下特点。
(有些地方不是特别懂,之后看完第六章应该会好点)
虚拟页
往往很大。- 4KB~2MB
- 访问一字节开销的原因才要这么大。
DRAM
缓存是全相联
- 也就是: 任何
虚拟页
都能放在任何物理页
中。 - 原因在于大的不命中惩罚
- 也就是: 任何
更精密的替换算法
- 替换错了虚拟页的惩罚很高。
DRAM
缓存总是写回
- 因为对磁盘的访问时间很长
- 而不用
直写
9.3.2 页表
判断命中和替换又多种软硬件联合提供。
操作系统软件,MMU中的地址翻译硬件和
页表(page table)
。页表
是存放在物理存储器的数据结构。页表
将虚拟页映射到物理页。- 地址翻译硬件将虚拟地址转换为物理地址都会读取
页表
。
操作系统负责维护
页表
的内容,以及磁盘及DRAM之间来回传送页。
页表
就是一个页表条目(Page Table Entry,PTE)
的数组.
- 虚拟地址空间 中每个页在页表的固定偏移量处都有一个
PTE
. - 每个
PTE
由一个有效位
和n位地址字段
。
有效位
表明虚拟页是否被缓存。
- 如果有效位存在,那么地址字段指向对应的物理存储器。
- 如果有效位不存在。
- 地址字段要么为NULL
- 要么指向虚拟页在磁盘所在的位置。
- 虚拟地址空间 中每个页在页表的固定偏移量处都有一个
9.3.3 页命中
- 一个页命中的过程。
- 一个虚拟地址转换为物理地址的过程。
9.3.4 缺页
在虚拟存储器的习惯说法中,DRAM缓存不命中称为缺页
。
处理过程如下:
- 读取虚拟地址所指向的
PT
。 - 读取
PTE
有效位,发现未被缓存,触发缺页异常。 调用缺页异常处理程序
- 选择牺牲页。
- 如果牺牲页发生了改变,将其拷贝回磁盘(因为是
写回
) - 需要读取的页代替了牺牲页的位置。
- 结果:牺牲也不被缓存,需要读取的页被缓存。
中断结束,重新执行最开始的指令。
- 在
DRAM
中读取成功。
虚拟存储器
是20世纪60年代发明的,因此即使与SRAM缓存使用了不同的术语。
块
被称为页
。- 磁盘和DRAM之间传送
页
的活动叫做交换(swapping)
或者页面调度(paging)
。 - 有
不命中
发生时,才换入页面,这种策略叫做按需页面调度(demand paging)
。
- 现代系统基本都是用这个。
9.3.5 分配页面
比如某个页面
所指向地址为NULL
,将这个地址指向磁盘某处,那么这就叫分配页面
。
此时虚拟页
从未分配
状态 变为 未缓存
。
9.3.6 又是局部性拯救了我们
虚拟存储器
工作的相当好,主要归功于老朋友局部性(locality)
尽管从头到尾的活动页面数量大于物理存储器大小。
但是在局部内,程序往往在一个较小的活动页面集合工作
这个集合叫做
工作集(working set)
或者叫常驻集(resident set)
- 初始载入开销比较大。
程序有良好的
时间局部性
,虚拟存储器
都工作的相当好。- 如果程序实在很烂,或者物理空间很小,
工作集
大于物理存储器
大小。这种状态叫颠簸(thrashing)
.
- 这时,页面不断换进换出。性能十分低。
统计缺页次数
可以利用Unix的getrusage
函数检测缺页数量。
9.4 虚拟存储器作为存储器的管理工具
实际上,操作系统为每个进程提供一个独立的页表
。
因此,VM
简化了链接
和加载
,代码
和数据共享
,以及应用程序的存储器
分配。
简化链接
独立的空间地址意味着每个进程的存储器映像使用相同的格式。
- 文本节总是从
0x08048000
(32位)处或0x400000
(64位)处开始。 - 然后是数据,bss节,栈。
- 文本节总是从
一致性极大简化了
链接器
的设计和实现。
简化加载
加载器
可以从不实际拷贝任何数据从磁盘到存储器。- 基本都是虚拟存储系统完成。
将一组连续的
虚拟页
映射到任意一个文件中的任意位置的表示法称作存储器映射
。Unix提供一个称为mmap
的系统调用,允许程序自己做存储器映射。在9.8详细讲解。
简化共享
- 独立地址空间为操作系统提供了一个管理用户进程和操作系统自身之间的一致
共享
机制. - 例子
- 操作相同的操作系统内核代码
- C标准库的
printf
.
- 因此操作系统需要将不同进程的适当的虚拟页映射到相同的物理页面。
- 多个进程共享这部分代码的一个拷贝。
- 而不是每个进程都要加载单独的内核和C标准库的拷贝。
- 独立地址空间为操作系统提供了一个管理用户进程和操作系统自身之间的一致
简化存储器分配.
- 即
虚拟页
连续(虚拟页还是单独的),物理页
可以不连续。使得分配更加容易。
- 即
9.5 虚拟存储器作为存储器保护的工具
任何现代操作系统必须为操作系统提供手段来控制对 存储器系统的访问。
- 不应该允许用户进程修改它的只读文本段。
- 不允许它读或修改任何内核的代码和数据结构
- 不允许读写其他进程的私有存储器。
- 不允许修改共享的虚拟页,除非所有共享者显示允许这么做(通过调用明确的进程间通信)
方式:在
PTE
上添加一些格外的许可位
来控制访问。SUP
:是否只有在内核模式下才能访问?READ
:读权限。WRITE
:写权限。
如果指令违反了许可条件,触发一般保护性异常,然后交给异常处理程序,
Shell
一般会报告为段错误(segmentaion fault)
。9.6 地址翻译
认识到硬件在支持虚拟存储器中的角色
以下是接下来可能要用到的符号,作参考。
形式上来说,地址翻译是一个N元素的虚拟地址空间(
VAS
)中的元素和一个M元素的物理地址空间(PAS
)元素之间的映射,以下展示了
MMU
(Memory Management Unit
,存储器管理单元)如何利用页表实现这样的功能页表基址寄存器(Page Table Base Register,PTBR)
指向当前页表。n
位