第九章
一些术语
PA(physical address)
:物理地址VA(virtual address)
:虚拟地址MMU(memory management unit)
:内存管理单元VP(virtual page)
:虚拟页PP(physical page)
:物理页/页帧SRAM
:表示位于CPU
和主存之间的L1
、L2
和L3
高速缓存DRAM
:表示虚拟内存系统的缓存,他在主存中缓存虚拟页PTE(page table entry)
:页表条目,页表就是一个页表条目数组VPO(virtual page offset)
:虚拟页面偏移量(每字节)VPN(virtual page number)
:虚拟页号VAS(virtual address sapce)
:虚拟地址空间PAS(physical address space)
:物理地址空间TLB(Transfer Lookaside buffer)
:快表、翻译后备缓冲器TLBI
:快表索引TLBT
:快表标记PPO(physical page offset)
:物理页面偏移量PPN(physical page number)
:物理页号CO
:缓冲块内的字节偏移量CI
:高速缓冲索引CT
:高速缓冲标记PTBR(page table base register)
:页表基址寄存器CR3(control register)
:控制寄存器(在四级页表中指向第一级页表的起始位置)vm_area_structs
:区域结构- 多级页表下的虚拟地址
- 将
VPN
分为了四项
PGD(page global directory)
:全局页目录项PUD(page upper directroy)
:上层页目录项PMD(page middle directroy)
:中间页目录项PTE(page table entry)
:页表项
虚拟地址空间与物理地址空间
- 虚拟地址空间
- 通常说的系统是
64
位的操作系统说的就是该虚拟地址空间大小为2 64 2^{64}264
- 物理地址空间
- 一般不要求是
2
的幂,计算机里面的C
盘大小就是物理地址空间
虚拟页与物理页
- 大小
- 一般来说页的大小都一样
- 两者的关系
- 虚拟页的状态
- 未分配的:未分配的页不占用任何磁盘空间
- 缓存的:已经缓存在物理内存中的页
- 未缓存的:已分配但未缓存在物理内存中的页
页表
- 页表就是一个页表条目(
PTE
)数组,
- 虚拟地址空间中的每个页在页表中一个固定偏移量处都有一个
PTE
- 页表条目
- 页表条目是由一个有效位和一个
n
位地址字段组成的。
- 有效位:表示该虚拟页是否被缓存在
DRAM
中 - 地址字段:表示
DRAM
相应物理页的起始位置
缺页
DRAM
的不命中被称为缺页
虚拟内存作为内存管理的工具
- 简化链接
- 代码段都是从
0x400000
开始,不需要关注物理内存
- 将不同进程中适当的虚拟页面映射到相同的物理页面
- 简化内存分配
虚拟内存与物理内存的翻译细节
- 虚拟内存被划分为:虚拟页号和虚拟页偏移量
- 快表标记与索引包含在虚拟页号中
假设快表是4路组相联,索引为2位,剩下的高位则是标记位
- 物理内存被划分为:物理页号和物理页偏移量
多级页表(具有层次结构的页表)
- 其是用来压缩页表的常用方法
- 原理:如果一级页表中的一个
PTE
是空的,那么相应的二级页表将不会存在
- 例子:四级页表
- 前三级页表分的
PTE
分别指向下一级的页表的物理地址,第四级页表的PTE
指向最终的物理页号
综合练习
如何将一个虚拟地址翻译为物理地址和访问缓存的
- 需要知道的东西
- 虚拟地址是多少位
- 虚拟地址会划分为
VPN
和VPO
- 物理地址是多少位
- 物理地址会划分为
PPN
和PPO
- 快表是多少路组相联的
- 例如是
4
路,则虚拟地址VPN
的低2
位就是TLBI
,剩余的高位就是TLBT
- 快表的作用就是一个缓存,会存放之前
cpu
引用过的PTE
副本(局部性原理)
L1cache
的结构是如何的
- 根据前面转换推出的
PPN
,然后根据第六章的知识就可以访问缓存中的数据了
例题:
已知:
- 内存按字节寻址
- 内存访问是针对
1
字节的字 - 虚拟地址是
14
位长 - 物理地址是
12
位长 - 页面大小是
64
字节 TLB
是四路组相联,总共有16
个条目L1 d-cache
是直接映射,行大小为4
字节,总共有16
个组
要求:写出CPU
执行了一条读地址0x3d4
处字节的加载指令发生了什么
处理已知信息
- 页面大小为
64
,则根据P568
页图9-12
可知虚拟内存与物理内存的划分
V P O = P P O = log 2 64 = 6 VPO = PPO = \log_264 = 6VPO=PPO=log264=6
剩下的高位就分别是VPN
与PPN
TLB
是四路组相联的,所以低2
位属于TLBI
快表组索引
T L B I = log 2 4 = 2 TLBI = \log_24 = 2TLBI=log24=2
所以高6 (14 - 6 - 2)
位是TLBT
快表行标记(用于锁定“行”)L1 d-cache
是直接映射,所以E = 1 E = 1E=1,行大小为4
字节所以B = 4 B = 4B=4,总共有16
个组,所以S = 16 S = 16S=16,m
则根据物理地址的位数求出为12
所以该存储器的结构为( 16 , 1 , 4 , 12 ) (16, 1, 4, 12)(16,1,4,12)
解题步骤:
0x3d4
展开将TLBT 、TLBI 、VPN 、VPO
标记在对应位置上
- 先根据快表相关信息去快表中查看发现命中
- 将
PPN(0x0D)
与VPO(0x14)
结合得出物理地址0x354
后划分物理地址然后访问缓冲
- 根据
CT、CI、CO
可以在告诉缓冲中找到目标值
缺页导致引发段错误的原因
- 访问到了
vm_area_structs
未定义的位置 - 一条试图对只读页面进行写操作
运行在用户模式的进程试图从内核虚拟内存中读取字
正常缺页
经过上面两条信息后,内核知道了这个缺页是对合法的虚拟地址进行合法的操作造成的
缺页操作流程
- 判断虚拟地址
A
是否合法:将A
和每个区域结构中的vm_start
和vm_end
相比较。如果不合法会触发一个段错误。 - 判断试图进行的内存访问是否合法:检查进程是否有读、写、执行此区域内页面的权限。如果不合法会触发一个保护异常。
- 选择牺牲页面并进行替换,如果牺牲页面被修改过需要先写回。
内存映射
内存映射:Linux
通过将一个虚拟内存区域与一个磁盘上的对象关联起来,来初始化这个虚拟内存区域的内容。
虚拟内存区域可以映射到两种类型的对象:
Linux
文件系统中的普通文件:一个区域可以映射到一个普通磁盘文件的连续部分,如一个可执行目标文件。如果区域比文件区要大,就用 0 填充剩下的部分。- 匿名文件:一个区域可以映射到一个匿名文件,匿名文件由内核创建,包含的全是二进制零。
无论哪种情况,一旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换文件(也叫交换空间)之间换来换去。
交换空间的大小限制着当前运行着的进程能够分配的虚拟页面的综述。
共享对象
- 一个对象可以被映射到虚拟内存的一个区域,要么作为共享对象,要么作为私有对象
写时拷贝
- 两个进程将一个私有对象映射到他们虚拟内存的不同区域,但是共享这个对象同一个物理副本。当有一个进程试图写私有区域内的某个页面,那么这个写操作就会触发一个保护故障。他会在物理内u才能中创建这个页面的一个新副本,更新页表条目指向这个新的副本,然后恢复这个页面的可写权限。
fork
函数原理
fork
是如何创建一个带有自己独立虚拟地址空间的新进程
- 当fork被调用时,内核为新进程创建各种数据结构,并且为,他分配一个唯一的
PID
,为了给这个新进程创建虚拟内存,他创建了当前进程的mm_struct
、区域结构和页表的原样副本。他将两个进程中的每个页面都标记为只读。并且将两个进程的每个区域结构vm_area_structs
都标记为私有的写时复制
当fork
在新进程中返回时,新进程现在的虚拟内存刚好和调用fork
时存在的虚拟内存相同,当两个进程的任意一个后来进行写操作时就会触发写时复制机制创建新页面。
execve
函数原理
例如执行execve("a.out", NULL, NULL);
- 删除已存在的用户区域
删除当前进程虚拟地址的用户部分中的已存在的区域结构 - 映射私有区域
- 映射共享区域
- 设置程序计数器
使用mmap
函数的用户级内存映射
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
- 让内核将一个包含
length
字节的权限为prot
且持有权限(私有还是共享)为flags
的文件(描述符为fd
)关联起来,相当于将磁盘上的一个文件映射到了进程虚拟地址上
malloc
返回值必须有对齐要求,这样就能存储任意数据类型的数据。但是这样会产生内部碎片
一些内存分配的实际问题
- 空闲块组织:如何记录空闲块
- 放置:如何选择一个合适的空闲块来防止一个新分配的块
- 分割:分配好一个块后如何处理空闲块的剩余部分
- 合并:如何处理一个刚刚被释放的块
堆块的格式
前提:块的大小必须双字对齐(8
字节)
- 前
32
位(4
字节)是块头部描述了块大小
由于块大小总是8
字节对齐,所以块大小的最后3
位总是0
,于是可以利用后三位来添加一些额外的信息,于是乎前29
位用来描述这个块的大小(包括头部和所有填充) - 后面就是有效载荷和填充
- 没有载荷时固定填充
4
字节与头部8
字节对齐
放置已分配块的策略
- 首次适配
- 最佳适配
- 下一次适配