操作系统虚拟内存

虚拟内存

虚拟内存的存在是为了运行比物理内存还大的程序,可以让系统看上去比实际物理空间大小大得多,并为多道程序的执行创造了条件。

明确一个概念:内存中的每一个存储单元都必须有确切的地址,而例如对32位机来说,能发出的地址数目就是2^32=4G,这也被称为处理器的寻址能力。因此机器内存的大小也应该是和处理器的最大寻址能力对应的。

但现实往往不是这样的,导致了大量寻址能力的浪费。但我们可以明确一点:程序总是被逐断运行的,一段时间内会稳定运行在某一段程序中。

因此一种做法是:把要运行的那一段程序从辅存(磁盘等外部存储设备)中复制出来,其余仍然留在辅存。当要运行下一段程序时,就把内存中的上一段程序换回到磁盘中。这也就让内存看起来变大了,这个存储空间也被叫做虚拟内存空间。这个虚拟内存空间是右寻址能力决定的。

这也就是说,一个程序被执行,需要执行两次映射,第一次映射到虚拟内存,第二次映射到物理内存。负责这个任务的硬件部分被称为存储管理单元(MMU),软件部分被称为内存管理模块。

内存管理模块记录着一个虚拟地址/物理地址映射表,作为MMU转换的依据。

虚拟内存技术

但以存储单元来管理显然不太现实,因此Linux把虚存空间分成若干个大小相等的存储分区,称为页。为了换入换出方便,物理内存也应该按照这种格式,物理内存的分块被称为页框。页与页框是Linux实现虚拟内存的基础。

物理内存地址,高位被称为页框码,低位被称为页框偏移量。
虚拟内存地址,低位被称为页码,低位被称为页内偏移量。

同时,应该还需要记录一个页表。在把一个页映射到某个页框的同时,就应该把对应的映射关系填入页表。

在地址转换的过程中,偏移量是不会变的。(先留个坑,再待探究)

请页与交换

虚存页面到物理页框的映射称为页面的加载。

当处理器试图访问一个虚拟内存页面时,先去查询是否已经映射到物理页框中,并记录到页表中。

  • 如果在,MMU(虚拟内存管理机制)会把页面转换成页框码,物理地址=页框码*页面大小+页偏移量,然后访问物理内存。
  • 如果不在,则发生页面访问错误,系统将判断该地址是否为有效地址。
    • 如果是有效地址,就从虚拟地址就将该地址指向的页面读入到内存的一个空闲页框中,并在页表上添加相关项
    • 如果是无效地址,则操作系统将终止本次访问

如果没有空闲物理页框了,就将执行缺页置换机制:

  • FIFO,先进先出
  • LFU,最不经常访问
  • LRU,最近一段时间最久没有访问

为了更加公平,Linux采用LRU算法,然后将页面移除,加入新的页面。

快表

访问虚拟内存时,每次都去页表里面找是很浪费时间的。但是人们发现,一旦访问了某个页,就会一段时间稳定工作在这个页上。于是系统提供了一个刚好能容纳一个页表的硬件寄存器,这样每次都先去寄存器中访问,速度就会快很多。我们称之为快表。

使用快表之后的地址转换流程是这样的:

  1. 根据虚拟地址中的页号查快表;
  2. 如果该页在快表中,直接从快表中读取相应的物理地址;
  3. 如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;
  4. 当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。

可以发现,虚拟内存不可能仅仅只通过软件来完成,必须配备一些硬件来承担一部分任务。

多级页表

一个程序在运行时,页表也要放在内存中。如果虚拟空间太大,就会导致页表太大,不可能说一次性全放到内存中。

所以应该再对页表进行分页存储,只把需要的页放到内存中。我们对页表建一个目录一样的东西,对应页表分页后的每个首地址,就可以进行查找。

当然,如果还是很大,就可以循环套娃,多建几级。

为了通用,Linux采用三级页表结构:页目录,中间页目录和页表。

分页和分段的异同

共同点:都是为了提高内存利用率,减少碎片。页和段在内存中是连续的。
不同点:页的大小是固定的,取决于操作系统;段的大小是不固定的,取决于运行的程序。

局部性原理

时间局部性:如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。

空间局部性 :一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。

补充

  1. 32位和64位的区别?
    CPU是通过物理总线访问内存,32位机就是32条总线,每条总线有高低两种电位0和1,可访问的最大地址就是2^32;64位机并没有64条总线,毕竟那真的太大了,因此最大内存要受限于操作系统。
  2. 虚拟内存更容易实现内存共享:每个进程都有自己的进程控制块和地址空间,以及与之对应的页表。两个不同的虚拟地址通过页表映射到物理空间的同一区域,即实现了内存共享。
作者

Benboby

发布于

2021-03-12

更新于

2021-03-13

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×