您當前的位置:首頁 > 動漫

Linux 頁表初探

作者:由 Linux碼農 發表于 動漫時間:2022-12-24

在虛擬記憶體中,頁表是個對映表的概念, 即從程序能理解的線性地址( linear address ) 對映到儲存器上的物理地址( phisical address )。

當一個程序在 CPU 上執行時,CPU 並不是直接訪問物理記憶體地址的,而是透過虛擬地址空間間接訪問物理記憶體的。

CPU 使用的地址都是虛擬地址,而資料存放在物理記憶體中,這個時候需要把虛擬地址轉換為實際的物理地址,然後根據物理地址從物理記憶體中獲取資料。

在作業系統中,虛擬地址轉換是透過一個稱為主存管理單元(Memory Management Unit, MMU)的硬體來提供轉換的。MMU就是透過頁表來進行查詢進行轉換成物理地址的。

Linux 頁表初探

本文對以 32 為邏輯地址空間的二級頁表進行分析。

二級頁表

系統採用二級頁表時,會把 32 位虛擬地址空間分為 3 個部分,如下:

Linux 頁表初探

頁目錄表也是佔用一個標誌的 4k 頁,由於每個頁目錄項佔用 4 個位元組,因此頁目錄表共有 1024 個表目錄項(Page Directory Entry, PDE)。

頁目錄表中的每個頁目錄項都記錄一個頁表的物理頁地址,物理頁地址是指頁的物理地址。而每個頁表中也是有 1024 個頁表項(Page Table Entry,PTE),每個頁表項中也是一個物理頁地址,最終資料寫在這個頁表項中指定的物理頁內。 而12 位的偏移量就是用來定址該物理頁中某個具體的儲存單元。

Linux 頁表初探

每個頁表項儲存的是一個物理頁框的物理地址,而每個頁框的基地址為 4k 的整數倍,因此物理地址的低12位無用的,所以把物理地址的低 12 位設定一些標誌位。

頁表項儲存的地址如下:

Linux 頁表初探

其中,頁框地址( PAGE FRAME ADDRESS )指定了一頁記憶體的物理起始地址。因為記憶體頁位於 4K 地

址邊界上,所以其低 12 位元總是 0,因此表項的低 12 位元可挪作它用。

在一個頁目錄表中,表項的頁框地址是一個頁表的起始地址;在第二級頁表中,頁表項的頁框地址則包含期望記憶體操作的物理記憶體頁

地址。

圖中的存在位(PRESENT – P)確定了一個頁表項是否可以用於地址轉換過程。P=1 表示該項可用。當目錄表項或第二級表項的 P=0 時,則該表項是無效的,不能用於地址轉換過程。此時該表項的所有其他位元位都可供程式使用;處理器不對這些位進行測試。

當 CPU 試圖使用一個頁表項進行地址轉換時,如果此時任意一級頁表項的 P=0,則處理器就會發出

頁異常訊號。此時缺頁中斷異常處理程式就可以把所請求的頁面對映和加入到物理記憶體中,並且導致異

常的指令會被重新執行。

已訪問(Accessed – A)和已修改(Dirty – D)位元位用於提供有關頁使用的資訊。除了頁目錄項中的已修改位,這些位元位將由硬體置位,但不復位。頁目錄項和頁表項的小區別在於頁表項有個已寫位

D(Dirty),而頁目錄項則沒有。

在對一頁記憶體進行讀或寫操作之前,CPU 將設定相關的目錄和二級頁表項的已訪問位。在向一個二

級頁表項所涵蓋的地址進行寫操作之前,處理器將設定該二級頁表項的已修改位,而頁目錄項中的已修

改位是不用的。當所需求的記憶體超出實際物理記憶體量時,記憶體管理程式就可以使用這些位來確定哪些頁

可以從記憶體中取走,以騰出空間。記憶體管理程式還需負責檢測和復位這些位元位。

讀/寫位(Read/Write – R/W)和使用者/超級使用者位(User/Supervisor – U/S)並不用於地址轉換,但用

於分頁級的保護機制,是由 CPU 在地址轉換過程中同時操作的。

經過上述分析,二級頁表地址轉換原理是將 32 位虛擬地址拆分成高 10 位,中間10 位,低 12 位三個部分。它們的作用是:高 10 位作為頁表的索引,用於在頁目錄表中定位一個頁目錄項 PDE,頁目錄項中有頁表的物理地址,也就是定位到某個頁表。中間 10 位作為物理頁的索引,用於在頁表內定位到某個頁表項 PTE,頁表項中有分配的物理頁地址,也就是定位到了某個物理頁。低 12 位作為頁內偏移量用於在已經定位到的物理頁內定址。

轉換過程如下:

用虛擬地址的高10位乘以4,作為頁目錄表內的偏移地址,加上頁目錄表的物理地址,所得的和,便是頁目錄項的物理地址。讀取該頁目錄項,從中獲取到頁表的物理地址。

用虛擬地址的中間10位乘以4,作為頁表內的偏移定址,加上在第一步中得到的頁表物理地址所得的和,便是頁表項的物理地址。讀取該頁表項,從中獲取分配的物理頁地址。

虛擬地址的高10位和中間10位分別是PDE和PTE的索引值,所以它們需要乘以4。但低12位就不是索引值啦,其表示的範圍為0~0xfff,作為頁內偏移最合適,所以虛擬地址的低12位加上第二步得到的物理頁地址,所得到的和便是最終轉換的物理地址。

Linux 頁表初探

上述中的頁目錄表和頁表都存在於物理記憶體中,每個表中項儲存的地址均為物理地址。二級頁表佔用的記憶體示意圖如下:

Linux 頁表初探

每個程序均有一個頁表,程序的頁表儲存在程序的pgd 欄位中

structmm_struct{

。。。。

pgd_t*pgd;

。。。。

}

該欄位為程序的頁全域性目錄的虛擬地址,當程序切換時,CPU 會把下一個程序頁表地址載入到CR3暫存器中,因此載入時會把 pgd 欄位中的虛擬地址轉換為物理地址。

asmvolatile(“movq%0,%%cr3”::“r”(next->pgd):“memory”);

程式被第一次載入排程執行時有個入口地址,該入口地址為虛擬地址,當cpu得到該程式的入口虛擬基地址執行時,會透過頁表(會先查詢轉換後援緩衝器TLB,又稱塊表,TLB是對頁表的快取,能夠快速查詢,TLB不命中,才會查頁表)查詢該虛擬地址對應的物理地址,然後透過物理地址獲取相應的資料,程式也就這樣一步一步的往下執行。

頁面換入和換出

我們知道,物理記憶體是寶貴且有限的。當程序執行的越來越多,記憶體使用量也就越來越多。這個時候會導致記憶體緊缺。為了解決這種記憶體不足情況,作業系統會採用一定的頁面替換演算法把暫時不用的物理記憶體替換出去,儲存到磁碟(交換裝置)中,為其他急用的資訊騰出空間,到需要時候再從磁碟上讀進來。

在分析頁面換入換出前,先了解下物理記憶體頁面的管理。

為了方便物理記憶體頁面的管理,每個記憶體頁面都對應一個 page 資料結構。在系統初始化階段,核心根據檢測到的物理記憶體的大小,為每一個頁面都建立一個 page 結構,形成一個page結構陣列,並使一個全域性量 mem_map 指向這個陣列。同時,又按需將這些頁面拼合成物理地址連續的許多記憶體頁面“塊”,在根據塊的大小建立起若干“管理區”(zone),而在每個管理區中則設定一個空閒塊列表,以便物理記憶體頁面的分配使用。

與此類似,交換裝置(通常是磁碟,也可是普通檔案)的每個物理頁面也要在記憶體中有個相應的資料結構,實際上只是一個計數,表示該頁面是否已被分配使用,以及有幾個使用者在共享這個頁面。對盤上頁面的管理是按檔案或磁碟裝置來進行的。核心中定義了一個 swap_info_struct 資料結構,用以描述和管理用於頁面交換的檔案或裝置。

structswap_info_struct{

unsignedintflags;

intprio;/*swappriority*/

structfile*swap_file;

structblock_device*bdev;

structlist_headextent_list;

structswap_extent*curr_swap_extent;

unsignedold_block_size;

unsignedshort*swap_map;

unsignedintlowest_bit;

unsignedinthighest_bit;

unsignedintcluster_next;

unsignedintcluster_nr;

unsignedintpages;

unsignedintmax;

unsignedintinuse_pages;

intnext;/*nextentryonswaplist*/

};

其中的指標 swap_map 指向一個數組,該陣列中的每個無符號短整型即代表盤上(或普通檔案中)的一個物理頁面,而陣列的下標則決定了該頁面在盤上或檔案中的位置。陣列的大小取決於 pages,它表示該頁面交換裝置或檔案的大小。裝置上(或檔案中,裝置也是一個檔案)的第一個頁面,也即是 swap_map[0] 所代表的那個頁面是不用於頁面交換的,它包括了該裝置或檔案自身的一些資訊以及表明哪些頁面可供使用的點陣圖。這些資訊最初是把該裝置格式化成頁面交換區時設定的。根據不同頁面交換區格式(以及版本),還有一些其他的頁面也不提供頁面交換使用。這些頁面都集中在開頭和結尾的地方,所以 swap_info_struct 中的 lowest_bit 和 hightest_bit 就說明檔案中從什麼地方開始到什麼地方為止是提供頁面交換使用的。另一個欄位max則表示該裝置或檔案中最大的頁面號,也就是裝置或檔案的物理大小。

Linux 核心允許使用多個頁面交換裝置(或檔案),所以在核心中建立了一個 swap_info_struct 結構陣列 swap_info。

staticstructswap_info_structswap_info[MAX_SWAPFILES];

就像透過 pte_t 資料結構(頁面表項)將物理記憶體頁面與虛擬頁面建立聯絡一樣,盤上頁面也有這麼一個 swp_entry_t 資料結構。

typedefstruct{

unsignedlongval;

}swp_entry_t;

一個 swp_entry_t 結構實際上只是一個 32 位無符號整數,這個整數分成3部分:

Linux 頁表初探

offset 表示頁面在一個磁碟裝置或檔案中的位置,也就是檔案中的邏輯頁面號;而type則是指該頁面在哪一個檔案中,是個序號(一個可以容納127個這樣的檔案,但實際上視系統的配置而定,遠小於127)。

swp_entry_t 和 pte_t 兩種資料結構大小相同,關係非常密切。當一個頁面在記憶體中時,頁面表中的表項pte_t的最低位P標誌為1,表示頁面在記憶體中,而其餘各位指明物理記憶體頁面的地址及頁面的屬性。當一個頁面在磁碟上時,則相應的頁面表項不再指向一個物理記憶體頁面,而是變成了一個 swp_entry_t 表項,指示著這個頁面的去向。由於此時其最低位為 0,表示頁面不在記憶體,所以 CPU 中的 MMU 單元對其餘各位都忽略不顧,而留待系統軟體自己來加以解釋。在 Linux 核心中,就用它來唯一地確定一個頁面在盤上的位置,包括在哪一個檔案或裝置,以及頁面在此檔案中的相對位置。

因此,當頁面在記憶體時,頁面表中的相應表項確定了地址的對映關係;當頁面不在記憶體中,則指明瞭物理頁面的去向和所在。

在頁面交換中,只有對映到使用者空間的頁面才會被換出,而核心,即系統空間的頁面不在此列。在核心中可以訪問所有的物理頁面,換言之所有的物理頁面在系統空間中都是有對映的。所謂“使用者空間的頁面”,是指在至少一個程序的使用者空間中有對映的頁面,反之則為(只能有)核心使用的頁面。

按頁面的內容和性質,使用者空間的頁面有如下幾種:

普通的使用者空間頁面,包括程序的程式碼段、資料段、堆疊段,以及動態分配的“儲存堆”。其中有些頁面從使用者程式及程序的角度看是靜態的(如程式碼段),但從系統的角度來看仍是動態分配的。

透過系統呼叫 mmap()對映到使用者空間的已開啟檔案的內容。

程序間的共享記憶體區。

這些頁面既涉及分配、使用和回收,也涉及頁面的換入和換出。

當系統挑選出若干記憶體頁面準備換出時,將這些頁面的內容寫入相應的磁碟頁面中,並且將相應的頁面表項的內容改成指向盤上頁面(P標誌位為0,表示頁面不在記憶體中),但是所佔據的記憶體頁並不立即釋放,而是將其 page 結構留在一個“暫存”(cache)佇列(或緩衝佇列)中,只是使其從“活躍狀態”轉入了“不活躍狀態”。至於其最後釋放,則推遲到以後有條件地進行。這樣,若在一個頁面被換出以後立即又受到訪問而發生異常,就可以從物理頁面的暫存佇列中找回相應的頁面,再次為之建立對映。這樣就不需要從盤上讀取了。

換入換出流程:

當 CPU 把虛擬地址發給 MMU 進行地址轉換時,MMU 發現頁表中沒有到物理記憶體的對映,則發生缺頁異常中斷,缺頁異常程式根據虛擬地址從盤上找到相應的盤頁面,然後從物理記憶體中查詢一個空閒的記憶體頁,若找到,則把盤頁面內容載入到物理記憶體中,然後修改頁表。若找不到空間的記憶體頁,則根據頁面替換演算法把一些物理記憶體頁換出到磁碟中,然後修改替換出去的記憶體頁對應的頁表項,頁表項內容指向替換到磁碟的位置(swp_entry_t )。

當訪問被替換出去的記憶體時,透過查詢頁表,根據表項pte_t的最低位P標誌位(為0,表示頁面不在記憶體),則把pte_t指向的地址轉換成swp_entry_t,然後從磁碟中載入換出的記憶體頁。

請求分頁虛擬地址轉換流程總結

MMU接收CPU傳過來的邏輯地址後並自動按頁面大小把它從某位起分解成2部分:頁號和頁內偏移;

以頁號為索引搜尋塊表TLB;

若命中,立即送出頁框號,並與頁內偏移量拼接成物理地址,然後進行訪問許可權檢查,若獲得透過,程序就可以訪問物理地址;

若不命中,由硬體以頁號為索引搜尋程序頁表,頁表基地址由硬體暫存器CR3 指出;

若在頁表中找到此頁面,說明所反問的頁面已在主存中,可送出頁框號,並與頁內偏移量拼接成物理地址,然後進行訪問許可權檢查,若獲得透過,程序就可以訪問物理地址,同時要把這個頁面資訊裝入塊表 TLB,以備再次訪問;

若發現頁表中對應的頁面失效或沒有,MMU 發出缺頁中斷,請求作業系統進行處理,MMU 工作到此結束。

MMU 發現缺頁併發出缺頁中斷,儲存管理接收控制,進行缺頁中斷處理的過程如下:

掛起請求的缺頁程序;

根據頁表項,找到存放此頁的磁碟物理地址;

檢視主存是否有空閒頁,若有則找出,修改主存管理表和相應頁表項的內容,轉到步驟6;

如果主存中無空閒頁框,按照頁面替換演算法選擇淘汰頁面,檢查其是否被寫過或修改過,若否轉到步驟6;若是轉到步驟5;

淘汰頁面被寫過或修改過,將其內容寫回磁碟的原先位置;

進行調頁,把頁面裝入主存所分配的頁框中,同時修改程序頁表項;

返回程序斷點,重新啟動被中斷的指令。

公眾號原文 連結

Linux 頁表初探

推薦公眾號

Linux碼農

文章閱讀

Linux ‘網路配置’ 和 ‘故障排除’ 命令總結

Linux 故障排查-測試網路埠連通性

你需要了解的55個網路概念

Linux 命令神器 lsof

linux ulimit 調優

Linux GDB的實現原理

服務端 TCP 連線的 TIME_WAIT 過多問題的分析與解決

redis 多執行緒實現原理

60秒內對 Linux 進行效能診斷

Linux 下的資源限制

歡迎關注公眾號

Linux碼農

,獲取更多幹貨

歡迎關注公眾號

Linux碼農

,獲取更多幹貨