虚拟内存

物理寻址和虚拟寻址

把物理内存分为M个连续的字节数组。分别标号为0,1,2…这就是物理地址。

CPU直接查找对应的物理地址来查找信息就是物理寻址。CPU直接对内存总线发送地址然后主存接收到以后返回内容给CPU,并放在寄存器里。

现在,CPU一般先生成一个虚拟地址,然后将虚拟地址发送给MMU(内存管理单元)翻译成物理地址,这时才发送给主存,它再返回地址中的内容。这叫虚拟寻址

简要地址翻译流程:

在内存中存放了一个查询表(称为页表),MMU通过读取查询表来动态翻译虚拟地址,表的内容由操作系统管理。(一般情况下,第一级页表就在MMU中,被称为TLB,这样速度更快)


地址空间

一个非负整数地址的有序集合。意思是:用自然数从0开始依次为一个地方编号,得到的数的有序集合就是地址空间。如果这个地址空间的元素是连续的整数,就称它为线性地址空间

现在,CPU通过映射n比特位得到了$2^n$个地址编号,形成的线性地址空间的大小是$2^n$。记作$N = 2^n$,最小地址是0,最大地址是$N-1$。这样形成的地址空间被叫做n位地址空间。它表示虚拟内存,现在的32位系统/64位系统即是这个意思。

内存也有一个空间,它是物理存在的。它的大小记作M,不要求一定为2的幂次。


我们已经知道是从虚拟地址到物理地址的映射了,页的实现就是映射实现的具体方式。

为了让虚拟内存中的地址可以顺利转换到物理地址,我们规定每4KB长度的地址称为一个,和地址一样,分为虚拟页和物理页(物理页又称为页帧)。虚拟页面的集合为{未分配的页,已缓存的页,未缓存的页},后两项统称已分配的页。我们把一个虚拟也的大小称为$P=2^p$

而映射的基本单位就是一个页。也就是说,一个进程在内存中是被划分为n页分开存放的(也可以挨在一起)

缓存表示已经分配到物理内存中,而未缓存表示在其它存储介质中,比如硬盘。如果系统查询页时发现是未缓存页,这称为未命中(缺页),然后要把那个页和内存中的一个物理页交换以访问它

页表

系统为了查询哪些页是已经分配的,哪些页是没有缓存的,以及页缓存在哪里等信息,同硬件/软件一起在内存中维护了一张页表。每个页表都是一个页表条目(PTE)数组

页表中每一个虚拟地址都对应了一个物理地址,也可能对应其它硬件,比如磁盘的某个位置。同时设置了一些标志来判断部分情况(比如可能0表示没有映射)

缺页

如果cpu查询页表发现它是一个未缓存页,就会引发一个缺页异常,从而进入内核态执行以下操作:

  • 选择一个页表中查询次数最少的页作为牺牲页
  • 如果它被修改了,那么写回磁盘
  • 将这次查询的物理页写到牺牲页对应的物理页中
  • 刷新页表,然后可以继续访问

内存管理

实际上,操作系统并不是只维护一张页表,而是对于每个进程都维护一张页表,每个的大小也都是4KB,从而形成了每个进程的独立虚拟地址空间。这样,每个程序的EP可以都是0x400000,而不用担心相互影响。

image-20240815201626132

内存保护

页表中有访问权限的标志位可以控制对对应页的访问,综合段寄存器,实现对段页权限的划分,提供安全等级


地址翻译

在这里说明虚拟地址转为物理地址的流程

任何虚拟地址都由两部分组成(物理地址也是):

虚拟页号和页偏移,长度分别为n-p-1和p-1

转换时,只转换虚拟页号,转为物理地址后+页偏移就得到真实的物理地址

image-20240815202956150

正如上面讲的,为了减少时间消耗,MMU中有一个翻译后备缓冲器TLB,它是一个物理设备,每一行都有一个由单个PTE组成的块,它可以直接被cpu查询

image-20240815203950975

多级页表

很明显,如果是32位系统,由于一个程序就有一个4MB的页表(一个页表项需要4字节,有4GB/4KB=$2^{20}$项),如果开了50个进程,就有200MB大小的空间,这显然是不能忍受的。

于是,使用多级页表可以实现压缩内存。首先:第一级页表在内存中,最常用的在TLB中,检索地址时,先检索TLB,如果没有,再检索内存,然后转到二级页表,被称为片(chunk)。再在片中查找基址再转到虚拟内存。

如果第一级页表不存在对应PTE,第二级页表页不会存在。
第二级页表不一定在内存中,只有最常用的才在。

这样就减轻了内存的负担。

实际上,使用的层级还更多


参考:

书籍:深入理解计算机系统

网站:

一篇文带你搞懂,虚拟内存、内存分页、分段、段页式内存管理(超详细)

你真的理解虚拟内存吗

CPU入门扫盲篇之MMU内存管理单元——万字长文带你搞定MMU&TLB&TWU