分頁表

分页表（page table）是一种数据结构，它用于计算机操作系统中的虚拟内存系统，其存储了虚拟地址到物理地址间的映射。虚拟地址在访问进程中是唯一的，而物理地址在硬件（比如内存）中是唯一的。

分页表的角色 

在操作系统中使用虚拟内存，每个进程会认为使用一块大的连续的内存。事实上，每个进程的内存散布在物理内存的不同区域。或者可能被调出到备份存储中(一般在硬盘)。当一个进程请求自己的内存，操作系统负责把程序生成的虚拟地址，映射到实际存储的物理内存上。操作系统在分页表中存储虚拟地址到物理地址的映射。每个映射被称为分页表项（page table entry，PTE）。

转换过程 

CPU的内存管理单元（memory management unit MMU）存储最近用过的映射缓存，来自操作系统分页表。被称为轉譯後備緩衝區（translation lookaside buffer, TLB）。TLB是一个索引缓存。

当需要将虚拟地址转换为物理地址时，首先搜索TLB。如果找到匹配（TLB命中），则返回物理地址并继续存储器访问。然而，如果没有匹配（称为TLB未命中），则存储器管理单元或操作系统TLB未命中处理器通常会查找页表中的地址映射以查看是否存在映射（页面遍历）。如果存在，则将其写回TLB（这必须完成，因为硬件通过虚拟存储器系统中的TLB访问存储器），并且重启错误指令（这也可以并行发生）。此后续转换将找到TLB命中，并且内存访问将继续。

转换失败 
有两种原因导致分页表查找失败。第一种，如果该地址没有可用的转换，这意味该虚拟地址的存储器访问是无效的。这通常是程序错误导致，操作系统需要处理这个问题。现代操作系统会发送一个段错误信号给出错程序。

当物理内存中不存在这个页，也会引起分页表查找失败。如果请求的页面被调出物理内存，给其他页腾出空间，会引起这个错误。这种情况下，页被分配到存储在介质上的辅助存储，例如硬盘。（这种辅助存储，或叫备用存储，如果是一个硬盘分区或者交换文件, 经常称之为交换分区，如果是文件，叫做分区文件或页文件。）这时候，分页需要从硬盘放回到物理内存中，这类操作通常会导致一种称为系统抖动（thrashing）的情况，通过局部性原理（principle of locality）来预测将来可能会访问的块，避免出现系统抖动。

当物理内存没满的时候，这是一个简单操作。页被写回物理内存，页表和转换备用缓冲会更新，指令重启。然而，当物理内存已满，一个或多个页要被调、为请求的页面腾出空间时候。页表需要更新，标识出那些在物理内存被调出的页，然后标识那些从硬盘调入物理内存的页。TLB也需要更新，包括去掉调出的页，重启指令。页的调入调出请见页置换算法。

分页表数据 
最简单的分页表系统通常维护一个帧表和一个分页表。帧表处理帧映射信息。更高级系统中，帧表可以处理页属于的地址空间，统计信息和其他背景信息。

分页表处理页的虚拟地址和物理帧的映射。还有一些辅助信息，如当前存在标识位(present bit)，脏数据标识位或已修改的标识位，地址空间或进程ID信息。

辅助存储，比如硬盘可以用于增加物理内存。页可以调入和调出到物理内存和磁盘。当前存在标识位可以指出那些页在物理内存，哪些页在硬盘上。页可以指出如何处理这些不同页。是否从硬盘读取一个页，或把其他页从物理内存调出。

脏数据标识位可以优化性能。一个页从硬盘调入到物理内存中，读取后调出，当页没有更改不需要写回硬盘。但是如果页在调入物理内存后被修改，会标记其为脏数据，意味着该页必需被写回备用存储。这种策略需要当页被调入到内存后，后备存储保留一份页的拷贝。有了脏数据标志位，一些页随时会同时存在于物理内存和后备存储中。

如果不是单地址空间操作系统，地址空间或进程id信息是必要的，内存管理系统需要知道页对应的进程。两个不同意图的进程可以使用两个相同的虚拟地址。分页表必需为两个进程提供不同的虚拟内存。用虚拟内存页关联进程ID页可以帮助选择要调出的页，当页关联到不活跃进程上，特别是那些主要代码页被调出的进程，相比活跃进程需要页的可能性会更小。

分页表类型 
有一些不同的分页表类型，适合不同应用场景。本质上，准系统分页表必需存储虚拟地址，物理地址排在之后，还有一些可能的地址空间信息。

倒排分页表 
倒排分页表(IPT)被认为是一个标准RAM的TLB的off-chip扩展。不同于一个真实的分页表，IPT不需要处理所有当前映射。操作系统要准备处理丢失，就像mips风格的软件填充TLB。

IPT把分页表和帧表结合成一个数据结构。其核心是一个固定大小的表，表的行数等于内存中的帧数。如果有4000个帧，倒排分页表有4000行。每个行的表项对应虚拟内存页码，物理页页码（不是物理地址），一些其他数据，创建碰撞链。

从所有IPT内核结构中搜索表项效率很低，所以我们用哈希表来映射虚拟地址（或可能需要的地址空间/PID信息）对应的IPT索引，这里会使用冲突链。哈希表被称作哈希锚点表。哈希方式没对覆盖率做通常的优化，原始速度更理想。当然，哈希表会有冲突。由于这个哈希方式，我们使用时候会经历很多冲突，所以表中VPN创建的每个表项，会检查是否是已被检索的表项或冲突。

在搜索映射时候，使用哈希锚点表。如果没有表项存在，会出现一个页错误。否则，表项被找到。依赖于架构，表项被重新放入TLB，内存指令重启，或跟踪冲突链直到结束，然后出现页错误。

这种模式种，一个虚拟地址可以分成2部分。前半段是虚拟页码，后半段是页中的偏移量。

这种设计的主要问题在于哈希方式cache命中率较弱。基于树的设计，忽略在分页表表项中置换相邻位置的相邻页。但是一个倒排分页表把表项离散，会破坏引用的空间局部性。为了减少这个问题，操作系统会使哈希表空间最小，平衡增长的未命中比率。通常会有一个哈希表，在物理内存中连续，共享给所有进程。内存碎片会导致预处理分页表不现实，所以用一个预处理标识消除不同进程的页的歧义。从进程中去掉分页表项或许有点慢，操作系统会忽略重用预处理标识值，延迟面对这个问题，选择忍受大量内存浪费，用于映射预处理哈希表。

多级分页表 
倒排分页表为所有物理内存中的帧，建立一个映射列表。但是这个可能会比较浪费。相反，我们通过保持一些覆盖当前虚拟内存区块的分页表，建立一个包含虚拟页映射的分页表数据结构。比如，我们创建1024个小于4K的页，覆盖4M的虚拟内存。

这非常有用，因为通常虚拟内存的顶部和底部用于正在运行的进程，顶部通常用于文本和数据段，底部用于堆栈，空闲内存居中。多级分页表会保持少量较小分页表，仅覆盖内存顶部和底部，只有确实需要时候才创建新的。每种较小分页表被一个主分页表链接再一起，有效的创建一个树型数据结构。需要不只2级，可能有多级。

这种模式下，一个虚拟地址可以分成三部分：根分页表的索引，子分页表的索引，子分页表偏移量。多级分页表也叫做分级分页表。

虚拟分页表 
已经提到了，在虚拟地址空间中为每个虚拟页创建分页表结构，最终会导致浪费。但是我们可以把分页表放入虚拟内存，允许虚拟内存管理分页表内存，来避免过多空间被涉及。

但是一部分这种线性分页表结构，需要一直存在于物理内存，来防范循环页错误。会寻找一个不存在于分页表中的重要部分，在当前分页表中没有。

嵌套分页表 
嵌套分页表可以被用于增加硬件虚拟化的性能。为分页表虚拟化提供硬件支持，会大大降低模拟的需要。x86下虚拟化当前的选择是Intel的扩展分页表特性，和AMD的快速虚拟索引特性。

参考文献

参见 

 轉譯後備緩衝區
 快取文件置換機制
 指针
 PaX
 W^X

虚拟内存