虚拟内存(3)

30 August 2025
7 mins

上一篇中我们研究了虚拟内存在理想状态下的工作模式,现在我们要结合实际案例,来进一步了解虚拟内存在具体环境下是怎么工作的:

Intel Core7/Linux内存系统

Core i7地址翻译

我们的系统是一个运行Linux的Intel Core i7。下图是其处理器的一个封装结构:

image.png

包括四个核,一个大的所有核共享的L3高速缓存,以及一个DDR3内存控制器。每个核中都有L1L2的高速缓存,还有TLB条目缓存。其中L1、L2、L3高速缓存都是物理寻址的,块大小为64字节。L1和L2是8路组相联,L3是16路组相联。页大小可以在启动时配置为4KB或4MB大小。这里Linux使用的是4KB的页。

下图则展示了Core i7地址翻译的概况:

image.png

由于不同层级的页表中存储的条目不同,所以对于第一级、第二级、第三级的条目格式和第四级的条目格式。其地址字段结构略有不同:

对于第一级、第二级、第三级的条目格式。当P=1时(Linux中总是P=1),地址字段包含一个40位的物理页号(PPN),它指向下一级页表的开始处。(注:由于物理页面是4KB对齐的,所以起始地址应该是4096的倍数):

image.png

对于第四级页表中条目的格式。当P=1,地址字段包括一个40位的PPN,指向物理内存中某一页的基地址。同样的,需要4KB对齐:

image.png

我们可以看到,PTE有三个权限位,用来控制对页的访问:

下图则反映了通过四级页表将虚拟地址翻译成物理地址的过程:

image.png

通常情况下,我们将地址翻译的分为两步:

  1. MMU将虚拟地址翻译成物理地址
  2. 将物理地址传送到L1高速缓存

然而,实际的硬件实现优化了地址翻译的过程,允许这两个步骤一定程度上的重叠进行。例如Core i7系统上的一个虚拟地址有12位的VPO,这些位和相应的物理地址的PPO相同。且有八路相联的、64个组和大小为63字节的缓存块的物理寻址的L1高速缓存。

因此每个物理地址有6个缓存偏移位和6个索引位。这12个位刚好和VPO相对应。当CPU翻译一个虚拟地址的时候,它发送VPN到MMU,发送VPO到L1。当MMU查找PTE的时候,L1高速缓存正在利用VPO位查找相应的组合块偏移,读出组中的8个标记的数据字。当MMU得到PPN时,缓存可以直接对这8个标记进行匹配。这样就极大程度的优化了地址翻译的过程。

Linux虚拟内存系统

我们现在需要对LInux的虚拟内存系统做一个简单的描述,以能够大致的了解一个实际的操作系统是怎么组织虚拟内存,并处理缺页的。

我们知道linux为每个进程维护了一个单独的虚拟地址空间,如下图所示:

image.png

在此之前我们从来没有讨论过,内核部分的虚拟内存,这一部分位于用户栈之上,现在我们需要进一步的去了解它。

内核虚拟内存包含内核中的代码和数据结构。内核虚拟内存中的某些区域被映射到所有进程共享的物理页面。例如,每个进程都共享内核的代码和数据结构。同时,内核将一片连续的虚拟地址空间映射到一片相同大小的物理地址空间,从而实现对虚拟内存的线性直接映射。这样对指定虚拟地址空间的访问,就可以通过固定的偏移映射来进行访问,而无需页表查找的模式。

内核虚拟内存的的其他区域则存储着每个进程都不相同的数据。如页表、内核在进程的上下文中执行代码时所用的栈,以及记录虚拟地址空间当前组织的各种数据结构。

虚拟内存区域

Linux将虚拟内存组织成一些区域(也叫做段)。区域实际上就是一片连续的已分配的虚拟内存页,往往不同的区域负责不同的内容。例如代码段、数据段、堆、共享库段、用户栈都是不同的区域。大大小小的区域有不同的意义,所以每个存在的虚拟页面一定是属于某个区域的。区域的概念使得虚拟地址空间之间可以有间隙,且不用记录(不用分配)那些不被使用的虚拟内核空间。

下图就是一个用于记录进程中虚拟内存区域的内核数据结构:

image.png

内核为系统中的每个进程维护了一个独立的任务结构(task_struct),任务结构中的元素包含或者指向内核运行这个进程所需要的所有信息(PID,指向用户栈的指针,可执行目标文件的名字,程序计数器…)

task_struct中的一个条目指向mm_struct,它描述了虚拟内存的当前状态。其中有两个我们感兴趣的字段:

Linux缺页异常处理

假设MMU在翻译某个虚拟地址A的时候,触发了一个缺页。这个异常会触发缺页处理程序,处理程序会执行下面的步骤:

image.png

内存映射

Linux通过将一个虚拟内存区域和一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程,我们就称之为内存映射。虚拟内存区域可以映射到两种类型的对象中的一种:

这里我们还要知道,一旦一个虚拟页面被初始化了,它就在一个由内核维护的专门的交换空间(swap file)之间换来换去。它相当于一个物理内存的页面的一个临时存储处,被替换的牺牲页被暂时的保存在这里。因此,交换空间限制着当前运行的进程能够分配的虚拟页面的总数。

共享对象

本章暂时结束。之后再回头搞一下,接下来要做一些实验和一些项目了。


← 初窥深度学习(4)

NJU_PA_STUDY(3) →