分頁表

分頁表（page table）是一種數據結構，它用於計算機操作系統中的虛擬內存系統，其存儲了虛擬地址到物理地址間的映射。虛擬地址在訪問進程中是唯一的，而物理地址在硬件（比如內存）中是唯一的。

**分頁表的角色**
Relationship between pages addressed by virtual addresses and the frames in physical memory, within a simple address space scheme. Physical memory can contain pages belonging to many processes. Pages can be paged to disk if used infrequently, or if physical memory is full. Not all pages are in physical memory in the above diagram.
在操作系統中使用虛擬內存，每個進程會認為使用一塊大的連續的內存。事實上，每個進程的內存散佈在物理內存的不同區域。或者可能被調出到備份存儲中(一般在硬盤)。當一個進程請求自己的內存，操作系統負責把程序生成的虛擬地址，映射到實際存儲的物理內存上。操作系統在分頁表中存儲虛擬地址到物理地址的映射。每個映射被稱為分頁表項（page table entry，PTE）。

**轉換過程**
虛擬地址到物理地址的轉換過程。 如果虛擬內存地址不存在於TLB，轉換會被重置並通過分頁表和硬件尋找。
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