什么是虚拟地址?
我们实际用到的物理地址就是主存,但是物理主存空间有限,所以一般现代操作系统都会想办法把一部分内存块放到磁盘中,用到的时候再装入主存,但是对用户程序而言,是不需要注意实际的物理内存的,为什么呢?因为有虚拟内存
的机制。
让操作系统为每个进程都分配一个虚拟地址,互不干涉。这样每个进程都不能访问物理地址,而是通过访问虚拟地址去映射到物理地址
操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。
一个进程最终要访问到哪个物理内存,由操作系统来管理
这里也引入两个概念
- 我们程序所使用的内存地址叫做虚拟内存地址
- 实际存在硬件里面的空间地址叫物理内存地址
进程持有的虚拟地址会通过CPU芯片中的内存管理单元(MMU)的映射关系来转换变成物理地址,然后在通过物理地址去访问内存
那操作系统怎么处理两者关系呢?引入了 内存分段和内存分页
内存分段
程序就是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段的形式把这些段分离出来
- 段选择子就保存在段寄存器里面。段选择子里面最重要的是段号,用作段表的索引。段表里面保存的是这个段的基地址、段的界限和特权等级等。
- 虚拟地址中的段内偏移量应该位于 0 和段界限之间,如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址。
我们知道分段的方法后,我们可以引入一个实例看一下:
先找到段基地址,然后加上偏移量。就可以正确的对应到物理内存中的地址了
这就是内存分段的方法,但是内存分段有缺点吗?
- 会产生 内存碎片 的问题
- 会产生 内存交换的效率低 的问题
探讨一下内存碎片问题
内存碎片分为 内部内存碎片和外部内存碎片:
- 内部内存碎片:已分配但是未使用的内存。就比如分配了8kb,但是程序只需要用6kb,空闲了2kb
- 外部内存碎片 :系统中存在的未分配未使用的内存。就比如需要6kb的,但是只有2kb,4kb,5kb的空闲块,虽然总内存是够得,但是因为不连续仍然没法使用
那内存分段会出现内存碎片吗?
内存分段管理会根据实际需求分配内存,所以不会产生内部内存碎片
但是因为每个段长度不固定,多个段未必能恰好使用所有空间,所以会出现外部内存碎片
探讨一下,内存交换效率低问题
因为上面说的容易产生外部内存碎片,那产生这种问题后,不得不进行Swap
内存区域,这个过程就会产生性能瓶颈了
Swag是一种辅助内存管理机制,用于扩展系统的可用内存容量。当物理内存不足以满足进程需求时,会将不常用的内存页临时移动到swag区域,以释放足够的武林内存给当前需要的进程。因为swag位于磁盘上,所以读取速度远低于内存。
所以,如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿。
针对这两个问题,又引入了内存分页机制
内存分页
要解决上述问题,关键在于减少内存碎片的出现。
采用内存分页,将整个虚拟和物理内存空间切成一段段固定尺寸的大小。
分页具体是怎么解决问题的?
内存分页由于内存空间都是预先划分好的,也就不会像内存分段一样,在段与段之间会产生间隙非常小的内存,这正是分段会产生外部内存碎片的原因。而采用了分页,页与页之间是紧密排列的,所以不会有外部碎片。
但是,因为内存分页机制分配内存的最小单位是一页,即使程序不足一页大小,我们最少只能分配一个页,所以页内会出现内存浪费,所以针对内存分页机制会有内部内存碎片的现象。
如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出(Swap Out)。一旦需要的时候,再加载进来,称为换入(Swap In)。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。
而且,我们在加载程序的时候,不需要把程序都加载到物理内存中。我们可以都缓冲在虚拟内存中,当真正需要用到的时候,在加载到物理内存中
多级页表
如果使用单一的页面会造成 页表占用特别大的内存空间。
首先我们要知道:页表必须要涵盖所有虚拟地址空间,不然如何虚拟地址在页表中找不到对应的页表项的时候,计算机系统就会停止工作了。
所以我们可以引入多级页面。
以两级页表为例子,第一级页表可以用来存储二级页表的目录,而第二级页表再用来存储实际的数据。
TLB
多级页表虽然解决了空间上的问题,但是由于虚拟地址到空间地址中间多道转换工序,也带来了时间上的开销
为了解决时间问题,在CPU芯片中,加入了一个专放程序最常访问的页表项的Cache,这个Cache就是TLB,通常被称为页表缓存、快表等。
这样以后CPU在寻址的时候,会先查看TLB,如果没查到,在去进行常规的页表检查
分页机制下,虚拟地址和物理地址之间如何映射的?
虚拟地址分为页号和页内偏移量,根据页号去页表中寻找对应的物理页号,拿到物理页号后加上前面上偏移量,就找到了物理内存
这种简单的分页会造成空间上的缺陷。页表会占用大量内存
段页式内存管理
将内存分段和内存分页组合起来使用,通常就被称为 段页式内存管理
- 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制
- 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页
这样,地址结构就由段号、段内页号和页内位移三部分组成。
那这访问到物理地址的过程是如何的呢?
- 先访问段表,得到页面起始地址
- 接着访问页面,得到物理页号
- 最后将物理页号与页内位移组合,得到了物理地址