声明:以下内容为学习小林图解网络所做的简单笔记,更多详情请关注小林coding学习
为什么需要虚拟内存
为了让系统可以运行更多的进程,进程之间不能互相干扰,所以进程之间需要占用不同的物理内存,解决的办法就是给每个进程分配一套虚拟内存,虚拟内存通过cpu的内存管理单元映射到物理内存。
程序使用的内存地址叫做虚拟地址
实际存在硬件中的空间地址叫做物理地址
为了管理虚拟地址与物理地址之间的关系
主要有以下两种方式
内存分段和内存分页
- 内存分段
内存分段的虚拟地址主要由两部分组成:
段选择因子和段内偏移量,段选择因子中记录了段号,通过段号可以找到段表,段表中记录了段基地址、段界限。段内偏移量是一个介于0和段界限之间的数值,通过段内偏移量+段基地址就可以找到物理地址了
优点:能产⽣连续的内存空间,使程序本身不需要关⼼具体的物理内存地址
缺点:1.第⼀个就是内存碎⽚的问题。
2.第⼆个就是内存交换的效率低的问题
外部内存碎⽚,也就是产⽣了多个不连续的⼩物理内存,导致新的程序⽆法被装载;
内部内存碎⽚,程序所有的内存都被装载到了物理内存,但是这个程序有部分的内存可能并不是很常使⽤,这也会导致内存的浪费;
- 内存分页
内存分页的虚拟地址由页号和页内偏移组成,根据页号在页表中找到虚拟地址对应的物理地址的基地址,通过页内偏移加上基地址就找到了物理地址
内存分页可以很好的解决内存分段出现的问题,分⻚是把整个虚拟和物理内存空间切成⼀段段固定尺⼨的⼤⼩。这样⼀个连续并且尺⼨固定的内存空间,我们叫⻚(Page)。在 Linux 下,每⼀⻚的⼤⼩为 4KB 。
通过页表来完成虚拟内存和物理内存的映射,页表是每个进程独有、存储在内存⾥的,通过内存管理单元 (MMU)完成将虚拟内存地址转换成物理地址的⼯作。
当进程访问的虚拟地址在⻚表中查不到时,系统会产⽣⼀个缺⻚异常,进⼊系统内核空间分配物理内存、更新进程⻚表,最后再返回⽤户空间,恢复进程的运⾏
分⻚是怎么解决分段的内存碎⽚、内存交换效率低的问题?
由于内存空间都是预先划分好的,程序在内存中按页存储,不会像分段那样产生非常小的内存间隙,分⻚的⽅式使得我们在加载程序的时候,不再需要⼀次性都把程序加载到物理内存中。我们完全可以在进⾏虚拟内存和物理内存的⻚之间的映射之后,并不真的把⻚加载到物理内存⾥,⽽是只有在程序运⾏中,需要⽤到对应虚拟内存⻚⾥⾯的指令和数据时,再加载到物理内存⾥⾯去。
多级页表
简单的分页会有空间上的缺陷,如果系统中运行的进程非常多,那就意味着页表占用的内存会很多。
简单的计算一下,在 32 位的环境下,虚拟地址空间共有 4GB,假设⼀个⻚的⼤⼩是 4KB(2^12),那么就需要⼤约 100 万
(2^20) 个⻚,每个「⻚表项」需要 4 个字节⼤⼩来存储,那么整个 4GB 空间的映射就需要有 4MB的内存来存储⻚表。
这 4MB ⼤⼩的⻚表,看起来也不是很⼤。但是要知道每个进程都是有⾃⼰的虚拟地址空间的,也就说都有⾃⼰的⻚表。
那么, 100 个进程的话,就需要 400MB 的内存来存储⻚表,这是⾮常⼤的内存了。
为了解决上述问题,引入了多级页表
比如说二级页表,将上述的100多万个页,先分成1024个一级页表,再将每个一级页表分成1024个页表项,形成二级页表。
如果某个⼀级⻚表的⻚表项没有被⽤到,也就不需要创建这个⻚表项对应的⼆级⻚表了,即可以在需要时才创建⼆级⻚表,实现节约内存的目的
那么为什么不分级的⻚表就做不到这样节约内存呢?
我们从⻚表的性质来看,保存在内存中的⻚表承担的职责是将虚拟地址翻译成物理地址。假如虚拟地址在⻚表中找不到对应的⻚表项,计算机系统就不能⼯作了。所以⻚表⼀定要覆盖全部虚拟地址空间,不分级的⻚表就需要有 100 多万个⻚表项来映射,⽽⼆级分⻚则只需要 1024 个⻚表项(此时⼀级⻚表覆盖到了全部虚拟地址空间,⼆级⻚表在需要时创建)。
引入多级页表带来的问题
虚拟地址到物理地址的映射多了几道转换的工序,也就是带来了时间上的开销,为了降低这种开销,引入了TLB(Translation Lookaside Buffer),或者称为⻚表缓存、快表等,将经常访问的页表项缓存在TLB中,有了 TLB 后,那么 CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的⻚表。
TLB 的命中率其实是很⾼的,因为程序最常访问的⻚就那么⼏个。
段⻚式内存管理
内存分段和内存分⻚并不是对⽴的,它们是可以组合起来在同⼀个系统中使⽤的,那么组合起来后,通常称为段⻚式内存管理
段⻚式内存管理实现的⽅式:
- 先将程序划分为多个有逻辑意义的段,也就是前⾯提到的分段机制
- 接着再把每个段划分为多个⻚,也就是对分段划分出来的连续空间,再划分固定⼤⼩的⻚;
虚拟地址结构由段号、段内⻚号和⻚内位移三部分组成。
段⻚式地址变换中要得到物理地址须经过三次内存访问:
第⼀次访问段表,得到⻚表起始地址;
第⼆次访问⻚表,得到物理⻚号;
第三次将物理⻚号与⻚内偏移组合,得到物理地址。
可⽤软、硬件相结合的⽅法实现段⻚式地址变换,这样虽然增加了硬件成本和系统开销,但提⾼了内存的利⽤率