一.前言
在之前研究进程上下文切换和golang内存分配器的过程中,发现虚拟内存在其中都扮演着十分重要的角色,之前有学习和了解过虚拟内存,但是随着时间推移也只知道一个概念,现在想要带着问题去再学习一遍虚拟内存,希望这篇文章也能帮助你们更好的理解虚拟内存。
1.进程初始化的时候是如何分配的内存
2.虚拟内存的具体工作流程
二.详细介绍
2.1 简介
虚拟内存是一个抽象概念,它为每个进程提供了一个假象,即每个进程都在独占的使用主存。每个进程看到的内存都是一致的,称为「虚拟地址空间」。
虚拟内存主要提供了三个能力
它将主存看做是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式高效使用了主存
它为每个进程提供了一致的地址空间,简化了内存管理
它保护了每个进程的地址空间不被其他进程破坏
2.2 如何工作
如图所示,cpu通过生产一个虚拟地址4001访问主存,虚拟地址再mmu的转换下映射为物理地址中的2,通过讲翻译后的物理地址传给主存,主存读取从物理地址2开始的4个字节将其返回给cpu。
2.3 名词解释
知道了大概的处理流程,接下来讲解一下虚拟内存相关的几个名词。
2.3.1 地址空间
一个非负整数地址的有序集合,在带有虚拟内存的系统中,cpu从一个有N=2n个地址的地址空间中生成虚拟地址,这个地址空间成为虚拟地址空间,要求是2的幂,一般由计算机的位数决定,如32位对应232,64对应2^64。
物理地址空间则对应的是系统中物理内存的M个字节,没有2的幂的要求
2.3.1 页表
页表将虚拟页映射到物理页,每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表,操作系统负责维护页表内容,以及在磁盘与DRAM(Dynamic Random Access Memory)之间来回传送页。
页面分配的三种情况
页表是一个页表条目(pte:page table entry)的数组,每一条由一个有效位和一个n位地址字段组成,有效位表明了该虚拟页是否被缓存在DRAM,如果有效,那么地址字段就表明了DRAM中的物理页起始地址。图中vp1,vp2,vp4都是这种情况。
若是空地址,则表明这个虚拟页还没有被分配,vp0就是这种情况。
若是有地址,但是有效位为0,那么就表明虚拟页被分配在了磁盘上,地址指向的就是虚拟页在磁盘的起始地址。
缺页
DRAM缓存不被命中就叫做缺页。
如果我们去访问vp3,就会发现vp3不在DRAM中,由于磁盘和DRAM的访问速度差了100000多倍,那么我们就需要将vp3缓存到DRAM中,此时我们将vp3从磁盘复制到物理内存(如果此时物理内存不够,就按照一定的算法将原本保存在里面的页面移出,并设置页表中对应的标记位为无效),再将vp3在页表中对应的pte有效位设置为有效,修改地址从磁盘指向DRAM。
进程页表
虚拟内存为每个进程提供了一个独立页表,因而是一个独立的虚拟地址空间,一来方便用户操作,二来方便用户共享内存,因为不同的页表支持映射到同一个共享物理页面上。
2.4 地址翻译
2.4.1 名词介绍
页表基址寄存器(page table base register PTBR):在cpu寄存器中指向当前页表(进程上下文切换中要更换)
n位虚拟地址:包含一个p位的虚拟页面偏移(virtual Page Offset,VPO)和一个n-p位的虚拟页号(virtual
page number,VPN),MMU利用VPN来选择适合的pte。物理页面的偏移量则与虚拟页面一致。
MMU(Memory Management Unit,内存管理单元):用于翻译虚拟地址和物理地址映射关系
2.4.2 地址翻译整体流程
1.cpu生成虚拟地址,传递给MMU
2.mmu生成pte地址(这一步是mmu通过虚拟地址的虚拟页号获取的),并从高速缓存/主存得到它
3.高速缓存/主存将pte返回给mmu
4.mmu通过页表,构造物理地址,将其传给高速缓存/主存
5.高速缓存/主存将请求的数据字给处理器
2.4.3 TLB
由上面的知识,每次获取内存mmu都需要去内存取一次pte,利用缓存思想(设计系统或者提高请求效率的时候都可以往这个上面靠,最基本原理就是空间换取时间),操作系统发明了tlb(translation lookaside buffer),他是一个关于pte的小缓存(注意进程上下文切换也需要清空tlb)。
有了tlb,如果发生命中,那么过程如下。
1.cpu生成虚拟地址,传递给MMU
2.mmu从tlb取出相应的pte
3.mmu将虚拟地址翻译成一个物理地址,并将它发送到高速缓存/贮存
4.高速缓存/主存将请求的数据字给处理器
三.问题思考
经过书本的系统学习,我已经了解了虚拟内存的工作原理,解决了问题二,还有进程初始化的时候是如何分配的内存这一问题。
以linux为例子,先看看在linux系统下进程的虚拟内存结构
进程能获取多少内存主要取决于运行时堆的内存分配,其余的内存消耗都是每个进程必要的,也就是说一个没有跑起来的进程消耗的内存都在物理内存上,等进程跑起来,会由操作系统对进程申请的内存进行动态分配,决定哪些放在物理内存,哪些放在主存,这是我们所不需要感知的。
四.参考文档
深入理解计算机系统
https://www.guru99.com/stack-vs-heap.html