参考书籍《深入理解计算机系统》
1 基本概念
1.1 寻址
- 物理地址:计算机系统的主存可以看做一个由M个连续的字节大小的单元组成的数组,每个字节都有一个唯一的地址,如下图:第一个地址为0,下一个为2,……,这个地址就是物理地址。
- 物理寻址:CPU使用物理地址访问存储器的方式就称为物理寻址。
- 虚拟寻址:CPU通过生成一个虚拟地址(VA)来访问主存,这个虚拟地址在被送到存储器之前先转换成适当的物理地址,这一过程叫虚拟寻址。将一个虚拟地址转换为物理地址的任务叫
地址翻译
,地址翻译需要CPU硬件和操作系统之间的紧密合作,CPU上有一个叫存储器管理单元(MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理。
1.2 地址空间
- 地址空间:是一个非负整数地址的有序集合。如果集合的整数是连续的,我们称之为
线性地址空间
。地址空间的大小是由最大地址的位数描述的,如N=2^n,则称为n位地址空间。 - 虚拟地址空间(VAS):CPU从一个有N=2^n个地址的地址空间生成虚拟地址,这个地址空间则称为虚拟地址空间。
- 物理地址空间(PAS):物理地址空间与系统中物理存储器的M个字节相对应。
1.3 虚拟存储器
- 虚拟存储器:一个系统中的所有进程是共享CPU和主存资源的,但存储器很容易被破坏,为了有效的管理存储器并少出错,现代系统提供了一种对主存的抽象概念,就叫虚拟存储器(VM)。
VM是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的、私有地址空间。 - 基本思想:每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间。主存中的每个字节都有一个选自虚拟地址空间的虚拟地址,和一个选自物理地址空间的物理地址。
1.4 页与页表
- 虚拟页:VM系统将虚拟存储器分割为大小固定的块,这些块就称为虚拟页。
- 页表:页表将虚拟页映射到物理页,每次地址翻译(即将虚拟地址转换为物理地址)都会读取页表,操作系统负责维护页表。
如下图,我们假设每个PTE是由一个有效位和一个n位地址字段组成的,有效位表明改虚拟页是否被缓存在DRAM中,如果是则地址字段则表示相应的物理页起始位置,否则表示虚拟页的起始位置。
1.5 缓存
- SRAM缓存:表示位于CPU和主存之间L1和L2的高速缓存;
- DRAM缓存:表示虚拟存储器系统的缓存,它在主存中,用来缓存虚拟页。
- 缓存速度:DRAM比SRAM慢10倍,磁盘比DRAM慢大概100 000多倍。
- 命中:DRAM缓存中的不命中比SRAM不命中开销大很多,而且磁盘的一个扇区读取第一个字节的时间开销比后面的大约慢100 000倍,因此虚拟页趋向于很大,典型为4~8KB,而且DRAM缓存是全相联的(即任何虚拟页可以放置在任何物理页中)。
- 写入方式:由于不命中时的替换错误处罚也非常高,所以操作系统对DRAM缓存用了更复杂精密的替换算法;由于对磁盘的访问时间很长,DRAM缓存总使用写回(write-back)而不是直写(write-through)。
2 虚拟存储器的作用
- 虚拟页面分类 :在任意时刻,虚拟页面的集合都分为三个不相交的子集:未分配的、缓存的、未缓存的。
如下图:编号为0和3的虚拟页还未被分配,没有任何数据和它们相关联,因此也不占用任何磁盘空间;编号1、4和6被缓存在物理存储器中;编号2、5和7已分配,但是并未缓存在主存中。
- 虚拟存储的作用
1.作为缓存的工具:利用DRAM缓存来自更大的虚拟地址空间的页面;
2.作为存储器保护的工具:除非所有共享者都显示的允许,否则不应该允许一个用户进程修改它的只读文本、读或修改任何内核代码或内核数据结构、读或写其他进程的私有存储器、修改任何与其他进程共享的虚拟页面,
3.作为存储器管理的工具:
1.简化链接:独立的地址空间允许每个进程为它的存储器映像使用相同的基本格式,而不用管代码和数据实际存放在物理存储器的何处;
2.简化共享:独立的地址空间为操作系统提供了管理用户进程和操作系统自身之间共享的一致机制;
3.简化存储器分配;
4.简化加载:虚拟存储器使加载可执行文件和共享目标文件到存储器中变得容易。
3 地址翻译
地址翻译本质是一个N元素的虚拟地址空间元素和一个M元素的物理地址空间元素之间的映射。
- 基本构成:
如下图CPU的一个控制寄存器 —— 页表基址寄存器(PTBR)指向当前页表;
虚拟地址包含两部分:p位的虚拟页面偏移(VPO)和(n-p)位的虚拟页号(VPN);
内存管理单元(MMU)利用VPN来选择适当的PTE,将页表条目中的物理页号(PPN)和虚拟地址中的VPO串联就得到相应的物理地址。
物理和虚拟页面大小相同,所有物理页面偏移(PPO)和VPO相同。
- 实现步骤:
1.处理器生成一个虚拟地址,并把它传送给MMU;
2.MMU生成PTE地址,并从高速缓存/主存请求得到它;
3.高速缓存/主存向MMU返回PTE;
4.MMU构造物理地址,并把它传送给高速缓存/主存;
5.高速缓存/主存返回所请求的数据字给处理器。 - 利用翻译后备缓冲器(TLB)加速地址翻译:
TLB是一个小的、虚拟寻址的缓存,其中每一行都保存一个由单个PTE组成的块,TLB通常有高度的相联性。实现步骤如下:
1.CPU产生一个虚拟地址;
2.MMU从TLB中取出相应的PTE;
3.MMU将这个虚拟地址翻译成物理地址,并将它发送给高速缓存/主存;
4.高速缓存/主存将所请求的数据字返回给CPU。
4 存储器映射 / 共享内存
- 映射:Linux通过将一个虚拟存储器区与一个磁盘上的对象关联起来,以初始化这个虚拟存储器区域的内容,这个过程称为存储器映射,虚拟存储器区域可以映射到两种类型的对象:unix文件系统中的普通文件和匿名文件。
- 使用mmap函数实现共享内存:
mmap函数要求内核创建一个新的虚拟存储器区域,并将文件描述符fd指定的对象的一个连续的组块(chunk)映射到这个新的区域
//头文件
#include <unistd.h>
#include <sys/mman.h>
//函数原型
extern void *mmap (void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset) __THROW;
//示例
buf = mmap(NULL,size, PROT_READ, MAP_PRIVATE|MAP_ANON, 0, 0)
//参数说明:
__addr:指定新映射的区域的起始地址,如果为null,则内核选择地址创建
__len:区域的大小(字节)
__prot:访问权限,取值如下:
PROT_EXEC:由可以被CPU执行的指令组成
PROT_READ:可读
PROT_WRITE:可写
PROT_NONE:不可访问
__flags:由描述被映射对象类型的位组成
MAP_ANON:此标志位被设置,且fd为NULL,则被映射的对象为匿名对象
MAP_PRIVATE:表示被映射对象是一个私有的写时拷贝对象
MAP_SHARED:表示一个共享对象
__fd:用来初始化新建区域的文件描述符
__offset:文件描述符中用来初始化的内容的偏移量
6 动态存储器分配 / 堆
- 动态存储器分配器维护一个进程的虚拟存储器区域,这个区域称为堆。分配器有两种:
1.显示分配器:要求应用显示的释放任何已分配的块(如malloc函数);
2.隐式分配器:也叫垃圾收集器,检测一个已分配的块不再被程序使用,则释放这个块(如JAVA的资源回收机制)。 - 显示分配器的要求:
1.处理任意请求序列;
2.立即响应请求;
3.只使用堆;
4.对齐块(8字节对齐);
5.不修改已分配的块。 - 显示分配器的目标:
1.最大化吞吐率;
2.最大化存储器利用。 - 碎片:内存碎片分为内部碎片和外部碎片。
- 合并空闲块:如果两个碎片相邻,我们称为假碎片,分配器会合并相邻的空闲块。