檢查程序頁表¶
pagemap 是核心中一套新(自 2.6.25 版本起)的介面,它允許使用者空間程式透過讀取 /proc 中的檔案來檢查頁表及相關資訊。
pagemap 有四個組成部分
/proc/pid/pagemap。此檔案允許使用者空間程序找出每個虛擬頁對映到的物理幀。它為每個虛擬頁包含一個 64 位值,包含以下資料(來自fs/proc/task_mmu.c,在 pagemap_read 之上)
位 0-54 頁幀號 (PFN)(如果存在)
位 0-4 交換型別(如果已交換)
位 5-54 交換偏移量(如果已交換)
位 55 pte 是軟髒的(請參閱 軟髒 PTE)
位 56 頁被獨佔對映(自 4.2 版本起)
位 57 pte 受 uffd-wp 防寫(自 5.13 版本起)(請參閱 Userfaultfd)
位 58 pte 是一個保護區域(自 6.15 版本起)(請參閱 madvise (2) 手冊頁)
位 59-60 零
位 61 頁是檔案頁或共享匿名頁(自 3.5 版本起)
位 62 頁已交換
位 63 頁存在
自 Linux 4.0 版本起,只有具有 CAP_SYS_ADMIN 能力的使用者才能獲取 PFN。在 4.0 和 4.1 版本中,非特權使用者的開啟操作會因 -EPERM 而失敗。從 4.2 版本開始,如果使用者不具有 CAP_SYS_ADMIN,則 PFN 欄位將被清零。原因:關於 PFN 的資訊有助於利用 Rowhammer 漏洞。
如果頁不存在但處於交換狀態,則 PFN 包含交換檔案號和頁在交換空間中的偏移量的編碼。未對映的頁返回空 PFN。這允許精確地確定哪些頁已對映(或處於交換狀態)並比較程序之間對映的頁。
傳統上,位 56 表示頁只被精確對映一次,當頁被多次對映時(即使在同一程序中被多次對映),位 56 會被清除。在某些核心配置中,作為較大分配(例如 THP)一部分的頁的語義可能會有所不同:如果相應較大分配的所有頁都確定地對映在同一程序中,即使該頁在該程序中被多次對映,位 56 也會被設定。當較大分配的任何頁可能對映在不同程序中時,位 56 會被清除。在某些情況下,即使不再是這種情況,較大的分配也可能被視為“可能被多個程序對映”。
此介面的高效使用者將使用
/proc/pid/maps來確定記憶體的哪些區域實際已對映,並使用 llseek 跳過未對映的區域。
/proc/kpagecount。此檔案包含一個 64 位計數,表示每個頁被對映的次數,並按 PFN 索引。某些核心配置不跟蹤作為較大分配(例如 THP)一部分的頁被對映的精確次數。在這些配置中,將返回此較大分配中每個頁的平均對映次數。但是,如果較大分配的任何頁已對映,則返回的值將至少為 1。
tools/mm 目錄中的 page-types 工具可用於查詢頁被對映的次數。
/proc/kpageflags。此檔案包含一個 64 位標誌集,用於每個頁,並按 PFN 索引。這些標誌是(來自
fs/proc/page.c,在 kpageflags_read 之上)
LOCKED(鎖定)
ERROR(錯誤)
REFERENCED(已引用)
UPTODATE(最新)
DIRTY(髒)
LRU
ACTIVE(活躍)
SLAB
WRITEBACK(回寫)
RECLAIM(回收)
BUDDY
MMAP(記憶體對映)
ANON(匿名)
SWAPCACHE(交換快取)
SWAPBACKED(交換支援)
COMPOUND_HEAD(複合頁頭)
COMPOUND_TAIL(複合頁尾)
HUGE(巨頁)
UNEVICTABLE(不可回收)
HWPOISON(硬體中毒)
NOPAGE(無頁)
KSM
THP
OFFLINE(離線)
ZERO_PAGE(零頁)
IDLE(空閒)
PGTABLE(頁表)
/proc/kpagecgroup。此檔案包含一個 64 位 inode 號,表示每個頁所屬的記憶體 cgroup,並按 PFN 索引。僅當 CONFIG_MEMCG 設定時可用。
頁標誌的簡短描述¶
- 0 - LOCKED(鎖定)
頁正在被鎖定以進行獨佔訪問,例如透過讀/寫 IO。
- 7 - SLAB
該頁由 SLAB/SLUB 核心記憶體分配器管理。當使用複合頁時,兩者都只在頭部頁上設定此標誌。
- 10 - BUDDY
由夥伴系統分配器管理的空閒記憶體塊。夥伴系統以不同階次的塊組織空閒記憶體。一個 N 階塊具有 2^N 個物理連續頁,BUDDY 標誌僅為第一頁設定。
- 15 - COMPOUND_HEAD(複合頁頭)
一個 N 階複合頁由 2^N 個物理連續頁組成。一個 2 階複合頁的形式為“HTTT”,其中 H 表示其頭部頁,T 表示其尾部頁。複合頁的主要消費者是 hugeTLB 頁(HugeTLB 頁)、SLUB 等記憶體分配器和各種裝置驅動程式。然而,在此介面中,只有巨頁/千兆頁對終端使用者可見。
- 16 - COMPOUND_TAIL(複合頁尾)
複合頁尾(見上述描述)。
- 17 - HUGE(巨頁)
這是 HugeTLB 頁的組成部分。
- 19 - HWPOISON(硬體中毒)
硬體檢測到此頁上的記憶體損壞:請勿觸碰資料!
- 20 - NOPAGE(無頁)
請求的地址處不存在頁幀。
- 21 - KSM
一個或多個程序之間動態共享的相同記憶體頁。
- 22 - THP
構造任意大小 THP 並以任意粒度對映的連續頁。
- 23 - OFFLINE(離線)
該頁邏輯上處於離線狀態。
- 24 - ZERO_PAGE(零頁)
pfn_zero 或 huge_zero 頁的零頁。
- 25 - IDLE(空閒)
該頁自被標記為空閒以來未被訪問(請參閱 空閒頁跟蹤)。請注意,如果該頁透過 PTE 訪問,則此標誌可能已過時。為確保標誌最新,必須首先讀取
/sys/kernel/mm/page_idle/bitmap。- 26 - PGTABLE(頁表)
該頁正在用作頁表。
其他注意事項¶
如果您沒有在 8 位元組邊界上開始讀取(例如,如果您在檔案中查找了奇數個位元組),或者如果讀取的大小不是 8 位元組的倍數,則從任何檔案中讀取都將返回 -EINVAL。
在 Linux 3.11 之前,pagemap 的位 55-60 用於“頁移位”(在大多數架構中始終為 12)。自 Linux 3.11 以來,在首次清除軟髒位後,它們的含義發生變化。自 Linux 4.2 以來,它們無條件地用於標誌。
Pagemap 掃描 IOCTL¶
pagemap 檔案上的 PAGEMAP_SCAN IOCTL 可用於獲取或可選地清除有關頁表條目的資訊。此 IOCTL 支援以下操作:
掃描地址範圍並獲取與所提供條件匹配的記憶體範圍。當指定輸出緩衝區時執行此操作。
防寫頁。
PM_SCAN_WP_MATCHING用於防寫感興趣的頁。如果發現非非同步防寫頁,PM_SCAN_CHECK_WPASYNC將中止操作。PM_SCAN_WP_MATCHING可以與PM_SCAN_CHECK_WPASYNC一起使用,也可以不使用。這兩個操作可以組合成一個原子操作,我們可以在其中獲取並防寫頁。
目前支援以下頁標誌:
PAGE_IS_WPALLOWED- 頁已啟用非同步防寫PAGE_IS_WRITTEN- 頁自被防寫以來已被寫入PAGE_IS_FILE- 頁由檔案支援PAGE_IS_PRESENT- 頁存在於記憶體中PAGE_IS_SWAPPED- 頁已交換PAGE_IS_PFNZERO- 頁具有零 PFNPAGE_IS_HUGE- 頁是 PMD 對映的 THP 或 HugeTLB 支援的PAGE_IS_SOFT_DIRTY- 頁是軟髒的PAGE_IS_GUARD- 頁是保護區域的一部分
struct pm_scan_arg 用作 IOCTL 的引數。
struct pm_scan_arg的大小必須在size欄位中指定。如果以後進行擴充套件,此欄位將有助於識別結構。標誌可以在
flags欄位中指定。目前只有PM_SCAN_WP_MATCHING和PM_SCAN_CHECK_WPASYNC是新增的標誌。獲取操作是可選執行的,取決於是否提供了輸出緩衝區。範圍透過
start和end指定。遍歷可能會在訪問完整範圍之前中止,例如使用者緩衝區可能已滿等。遍歷結束地址在
end_walk中指定。
struct page_region陣列的輸出緩衝區及其大小在vec和vec_len中指定。可選的最大請求頁數在
max_pages中指定。掩碼在
category_mask、category_anyof_mask、category_inverted和return_mask中指定。
查詢已被寫入的頁並將其防寫
struct pm_scan_arg arg = {
.size = sizeof(arg),
.flags = PM_SCAN_CHECK_WPASYNC | PM_SCAN_CHECK_WPASYNC,
..
.category_mask = PAGE_IS_WRITTEN,
.return_mask = PAGE_IS_WRITTEN,
};
查詢已被寫入、由檔案支援、未交換且存在或為巨頁的頁
struct pm_scan_arg arg = {
.size = sizeof(arg),
.flags = 0,
..
.category_mask = PAGE_IS_WRITTEN | PAGE_IS_SWAPPED,
.category_inverted = PAGE_IS_SWAPPED,
.category_anyof_mask = PAGE_IS_PRESENT | PAGE_IS_HUGE,
.return_mask = PAGE_IS_WRITTEN | PAGE_IS_SWAPPED |
PAGE_IS_PRESENT | PAGE_IS_HUGE,
};
PAGE_IS_WRITTEN 標誌可以被認為是 soft-dirty 標誌的一個性能更好的替代方案。它不受核心 VMA 合併的影響,因此使用者可以在普通頁的情況下找到真正的軟髒頁。(對於 THP 或 Hugetlb 頁,仍可能報告額外的髒頁。)
“PAGE_IS_WRITTEN” 類別與啟用 uffd 防寫的範圍一起使用,以在使用者空間中實現記憶體髒頁跟蹤
userfaultfd 檔案描述符透過
userfaultfd系統呼叫建立。
UFFD_FEATURE_WP_UNPOPULATED和UFFD_FEATURE_WP_ASYNC功能透過UFFDIO_APIIOCTL 設定。記憶體範圍透過
UFFDIO_REGISTERIOCTL 以UFFDIO_REGISTER_MODE_WP模式註冊。然後,註冊記憶體的任何部分或整個記憶體區域必須使用帶有
PM_SCAN_WP_MATCHING標誌的PAGEMAP_SCANIOCTL 或UFFDIO_WRITEPROTECTIOCTL 進行防寫。兩者執行相同的操作。前者在效能方面更好。現在,
PAGEMAP_SCANIOCTL 可以用於查詢自上次標記以來已被寫入的頁,和/或可選地防寫這些頁。