HugeTLB 頁

概述

本文件旨在簡要概述 Linux 核心中的 hugetlbpage 支援。此支援構建在大多數現代架構提供的多頁面大小支援之上。例如,x86 CPU 通常支援 4K 和 2M(如果架構支援,則為 1G)頁面大小,ia64 架構支援多種頁面大小 4K、8K、64K、256K、1M、4M、16M、256M,而 ppc64 支援 4K 和 16M。TLB 是虛擬到物理地址轉換的快取。通常,這是處理器上非常稀缺的資源。作業系統嘗試充分利用有限數量的 TLB 資源。隨著越來越大的物理記憶體(幾 GB)更容易獲得,這種最佳化現在變得更加關鍵。

使用者可以透過使用 mmap 系統呼叫或標準 SYSV 共享記憶體系統呼叫(shmget、shmat)來使用 Linux 核心中的大頁支援。

首先,Linux 核心需要使用 CONFIG_HUGETLBFS(位於“檔案系統”下)和 CONFIG_HUGETLB_PAGE(選擇 CONFIG_HUGETLBFS 時自動選擇)配置選項進行構建。

/proc/meminfo 檔案提供有關核心大頁池中持久 hugetlb 頁總數的資訊。它還顯示預設的大頁大小以及有關預設大小的大頁池中空閒、保留和剩餘大頁數量的資訊。大頁大小是生成系統呼叫的引數的正確對齊和大小所必需的,這些系統呼叫對映大頁區域。

cat /proc/meminfo 的輸出將包括如下行

HugePages_Total: uuu
HugePages_Free:  vvv
HugePages_Rsvd:  www
HugePages_Surp:  xxx
Hugepagesize:    yyy kB
Hugetlb:         zzz kB

其中

HugePages_Total

是大頁池的大小。

HugePages_Free

是池中尚未分配的大頁數量。

HugePages_Rsvd

是“保留”的縮寫,並且是已承諾從池中分配的大頁數量,但尚未進行任何分配。保留的大頁保證應用程式能夠在缺頁時從大頁池中分配一個大頁。

HugePages_Surp

是“剩餘”的縮寫,並且是池中超出 /proc/sys/vm/nr_hugepages 中的值的大頁數量。剩餘大頁的最大數量由 /proc/sys/vm/nr_overcommit_hugepages 控制。注意:當啟用釋放與每個 hugetlb 頁關聯的未使用 vmemmap 頁的功能時,當系統處於記憶體壓力下時,剩餘大頁的數量可能會暫時大於剩餘大頁的最大數量。

Hugepagesize

是預設的大頁大小(以 kB 為單位)。

Hugetlb

是所有大小的大頁消耗的記憶體總量(以 kB 為單位)。如果使用了不同大小的大頁,則此數字將超過 HugePages_Total * Hugepagesize。要獲得更詳細的資訊,請參閱 /sys/kernel/mm/hugepages(如下所述)。

/proc/filesystems 還應顯示核心中配置的“hugetlbfs”型別的檔案系統。

/proc/sys/vm/nr_hugepages 指示核心大頁池中當前“持久”大頁的數量。“持久”大頁在任務釋放時將返回到大頁池。具有 root 許可權的使用者可以透過增加或減少 nr_hugepages 的值來動態分配更多或釋放一些持久大頁。

注意:當啟用釋放與每個 hugetlb 頁關聯的未使用 vmemmap 頁的功能時,當系統處於記憶體壓力下時,我們可能無法釋放使用者觸發的大頁。請稍後再試。

用作大頁的頁面在核心中保留,不能用於其他目的。在大記憶體壓力下,大頁無法交換出去。

一旦將大量大頁預分配到核心大頁池,具有適當許可權的使用者就可以使用 mmap 系統呼叫或共享記憶體系統呼叫來使用大頁。請參閱下面的 使用大頁 的討論。

管理員可以透過指定“hugepages=N”引數在核心啟動命令列上分配持久大頁,其中“N”=請求的大頁數。這是分配大頁最可靠的方法,因為記憶體尚未碎片化。

某些平臺支援多種大頁大小。要分配特定大小的大頁,必須在大頁引導命令引數前加上大頁大小選擇引數“hugepagesz=<size>”。 <size> 必須以位元組為單位指定,並帶有可選的比例字尾 [kKmMgG]。可以使用 “default_hugepagesz=<size>” 引導引數選擇預設的大頁大小。

Hugetlb 引導命令列引數語義

hugepagesz

指定大頁大小。與 hugepages 引數結合使用,以預分配指定數量的指定大小的大頁。因此,hugepagesz 和 hugepages 通常以配對方式指定,例如

hugepagesz=2M hugepages=512

對於特定的大頁大小,hugepagesz 只能在命令列上指定一次。有效的大頁大小取決於體系結構。

hugepages

指定要預分配的大頁數量。這通常遵循有效的 hugepagesz 或 default_hugepagesz 引數。但是,如果 hugepages 是第一個或唯一一個 hugetlb 命令列引數,它將隱式指定要分配的預設大小的大頁數量。如果預設大小的大頁數量是隱式指定的,則不能透過預設大小的 hugepagesz,hugepages 引數對來覆蓋它。此引數還具有節點格式。節點格式指定要在特定節點上分配的大頁數量。

例如,在具有 2M 預設大頁大小的體系結構上

hugepages=256 hugepagesz=2M hugepages=512

將導致分配 256 個 2M 大頁,並顯示一條警告訊息,指示 hugepages=512 引數被忽略。如果 hugepages 引數前面有一個無效的 hugepagesz 引數,它將被忽略。

節點格式示例

hugepagesz=2M hugepages=0:1,1:2

它將在 node0 上分配 1 個 2M 大頁,在 node1 上分配 2 個 2M 大頁。如果節點號無效,則該引數將被忽略。

hugepage_alloc_threads

指定在引導期間應用於分配大頁的執行緒數。當分配大量大頁時,可以使用此引數來提高系統啟動時間。

預設值為可用硬體執行緒的 25%。使用 8 個分配執行緒的示例

hugepage_alloc_threads=8

請注意,此引數僅適用於非巨型大頁。

default_hugepagesz

指定預設的大頁大小。此引數只能在命令列上指定一次。 default_hugepagesz 可以選擇性地跟在 hugepages 引數之後,以預分配特定數量的預設大小的大頁。如上面的 hugepages 部分所述,也可以隱式指定要預分配的預設大小的大頁的數量。因此,在具有 2M 預設大頁大小的體系結構上

hugepages=256
default_hugepagesz=2M hugepages=256
hugepages=256 default_hugepagesz=2M

都將導致分配 256 個 2M 大頁。有效的預設大頁大小取決於體系結構。

hugetlb_free_vmemmap

當設定了 CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP 時,這將啟用 HugeTLB Vmemmap 最佳化 (HVO)。

當支援多種大頁大小時,/proc/sys/vm/nr_hugepages 指示當前預分配的預設大小的大頁的數量。因此,可以使用以下命令來動態分配/取消分配預設大小的持久大頁

echo 20 > /proc/sys/vm/nr_hugepages

此命令將嘗試將大頁池中預設大小的大頁數量調整為 20,根據需要分配或釋放大頁。

在 NUMA 平臺上,核心將嘗試將大頁池分佈在修改 nr_hugepages 的任務的 NUMA 記憶體策略指定的允許節點集上。當任務具有預設記憶體策略時,允許節點的預設設定為所有具有記憶體的線上節點。當分配持久大頁時,將靜默跳過可用連續記憶體不足以容納大頁的允許節點。請參閱下面有關任務記憶體策略、cpuset 和每個節點屬性與持久大頁的分配和釋放互動的 討論

大頁分配的成功與否取決於系統中存在物理連續記憶體的數量。如果核心無法從 NUMA 系統中的某些節點分配大頁,它將嘗試透過在其他具有足夠可用連續記憶體的節點上分配額外的頁面來彌補差異(如果有的話)。

系統管理員可能希望將此命令放入其中一個本地 rc init 檔案中。這將使核心能夠在啟動過程的早期分配大頁,此時獲得物理連續頁面的可能性仍然很高。管理員可以透過檢查 sysctl 或 meminfo 來驗證實際分配的大頁數量。要檢查 NUMA 系統中大頁的每個節點分佈,請使用

cat /sys/devices/system/node/node*/meminfo | fgrep Huge

/proc/sys/vm/nr_overcommit_hugepages 指定大頁池可以增長到多大,如果應用程式請求的大頁超過 /proc/sys/vm/nr_hugepages。將任何非零值寫入此檔案表明,當持久大頁池耗盡時,hugetlb 子系統允許嘗試從核心的普通頁面池中獲取該數量的“剩餘”大頁。當這些剩餘大頁變得未使用時,它們將被釋放回核心的普通頁面池。

當透過 nr_hugepages 增加大頁池大小時,任何現有的剩餘頁面將首先提升為持久大頁。然後,如果需要且可能,將分配額外的大頁以滿足新的持久大頁池大小。

管理員可以透過將 nr_hugepages sysctl 設定為較小的值來縮小預設大頁大小的持久大頁池。核心將嘗試在修改 nr_hugepages 的任務的記憶體策略中的所有節點上平衡大頁的釋放。所選節點上的任何空閒大頁都將釋放回核心的普通頁面池。

警告:透過 nr_hugepages 縮小持久大頁池,使其小於使用中的大頁數,會將使用中的大頁的平衡轉換為剩餘大頁。即使剩餘頁面的數量超過過度提交值,也會發生這種情況。只要此條件成立(即,直到 nr_hugepages+nr_overcommit_hugepages 充分增加,或者剩餘大頁不再使用並被釋放),將不允許分配更多的剩餘大頁。

由於執行時可用的多個大頁池的支援,/proc/sys/vm 中的大多數大頁使用者空間介面已在 sysfs 中複製。為了向後相容性,保留了上面討論的 /proc 介面。 sysfs 中的根大頁控制目錄是

/sys/kernel/mm/hugepages

對於正在執行的核心支援的每個大頁大小,都將存在一個子目錄,其形式為

hugepages-${size}kB

在這些目錄中的每一箇中,都將存在 /proc 中包含的一組檔案。此外,還可能存在兩個額外的用於降級大頁的介面

demote
demote_size
nr_hugepages
nr_hugepages_mempolicy
nr_overcommit_hugepages
free_hugepages
resv_hugepages
surplus_hugepages

降級介面提供將大頁拆分為更小的大頁的能力。例如,x86 架構同時支援 1GB 和 2MB 大頁大小。 1GB 大頁可以拆分為 512 個 2MB 大頁。降級介面不適用於最小的大頁大小。降級介面是

demote_size

是降級頁面大小。當降級頁面時,將建立相應數量的 demote_size 大頁。預設情況下,demote_size 設定為下一個較小的大頁大小。如果有多個較小的大頁大小,則 demote_size 可以設定為任何這些較小的大小。僅允許小於當前大頁大小的大頁大小。

demote

用於降級多個大頁。具有 root 許可權的使用者可以寫入此檔案。可能無法降級請求的大頁數量。要確定實際降級的頁面數量,請比較寫入降級介面之前和之後 nr_hugepages 的值。 demote 是一個只寫介面。

/proc 中相同的介面(除了 demote 和 demote_size 之外的所有介面)的函式與上述預設大頁大小的情況類似。

任務記憶體策略與大頁分配/釋放的互動

無論是透過 /proc 介面還是透過使用 nr_hugepages_mempolicy 屬性的 /sysfs 介面分配和釋放大頁到核心大頁池,從中分配或釋放大頁的 NUMA 節點都由修改 nr_hugepages_mempolicy sysctl 或屬性的任務的 NUMA 記憶體策略控制。當使用 nr_hugepages 屬性時,mempolicy 將被忽略。

使用 nr_hugepages 的上述示例中,將大頁分配或釋放到/從核心大頁池的推薦方法是

numactl --interleave <node-list> echo 20 \
                            >/proc/sys/vm/nr_hugepages_mempolicy

或者,更簡潔地說

numactl -m <node-list> echo 20 >/proc/sys/vm/nr_hugepages_mempolicy

這將分配或釋放 <node-list> 中指定的節點上的 abs(20 - nr_hugepages),具體取決於持久大頁的數量最初是小於還是大於 20。不會在指定的 <node-list> 中未包含的任何節點上分配或釋放大頁。

當透過 nr_hugepages_mempolicy 調整持久大頁計數時,可以使用任何記憶體策略模式——繫結、首選、本地或交錯。對持久大頁分配產生的效果如下

  1. 無論 mempolicy 模式如何 [請參閱 NUMA 記憶體策略],持久大頁將分佈在 mempolicy 中指定的節點上,就好像指定了 “交錯”一樣。但是,如果策略中的節點不包含足夠連續的記憶體來容納大頁,則分配將不會“回退”到具有足夠連續記憶體的最近鄰居節點。這樣做會導致大頁池的分配出現不希望的失衡,或者可能在任務記憶體策略不允許的節點上分配持久大頁。

  2. 可以使用繫結或交錯策略指定一個或多個節點。如果使用首選策略指定了多個節點,則僅使用最低的數字 ID。本地策略將選擇在構造 nodes_allowed 掩碼時任務正在執行的節點。為了使本地策略具有確定性,必須將任務繫結到單個節點中的 CPU 或 CPU。否則,任務可能在啟動後隨時遷移到其他節點,並且生成的節點將是不確定的。因此,本地策略對於此目的不是很有用。可以使用任何其他 mempolicy 模式來指定單個節點。

  3. 允許的節點掩碼將從任何非預設任務 mempolicy 派生,無論此策略是由任務本身還是其祖先(如 numactl)顯式設定的。這意味著,如果任務是從具有非預設策略的 shell 呼叫的,則將使用該策略。可以使用 numactl --interleave 或 --membind [-m] 指定 “all” 的節點列表,以實現在系統或 cpuset 中的所有節點上進行交錯。

  4. 任何指定的任務 mempolicy(例如,使用 numactl)都將受到任務執行的任何 cpuset 的資源限制的約束。因此,對於在具有系統節點子集的 cpuset 中執行的具有非預設策略的任務,如果沒有首先移動到包含所有所需節點的 cpuset,則無法在 cpuset 之外分配大頁。

  5. 啟動時大頁分配嘗試將請求的大頁數量分佈在所有具有記憶體的線上節點上。

每個節點的大頁屬性

上面描述的 sysfs 中根大頁控制目錄的一部分內容將在每個 NUMA 節點的系統裝置下複製,其中包含

/sys/devices/system/node/node[0-9]*/hugepages/

在此目錄下,每個受支援的大頁大小的子目錄都包含以下屬性檔案

nr_hugepages
free_hugepages
surplus_hugepages

free_’ 和 surplus_’ 屬性檔案是隻讀的。它們分別返回父節點上空閒和剩餘 [過度提交] 大頁的數量。

nr_hugepages 屬性返回指定節點上的大頁總數。當寫入此屬性時,無論任務的 mempolicy 或 cpuset 約束如何,父節點上的持久大頁數量都將調整為指定值(如果存在足夠的資源)。

請注意,過度提交和保留頁面的數量仍然是全域性量,因為我們直到出現故障時才知道,此時將應用故障任務的 mempolicy,從哪個節點嘗試進行大頁分配。

hugetlb 可以在以下情況下在每個節點的大頁池之間遷移:記憶體離線、記憶體故障、長期固定、系統呼叫(mbind、migrate_pages 和 move_pages)、alloc_contig_range()alloc_contig_pages()。現在只有記憶體離線、記憶體故障和系統呼叫允許回退以在不同的節點上分配新的 hugetlb,如果當前節點無法在 hugetlb 遷移期間分配,這意味著這 3 種情況可能會破壞每個節點的 hugepages 池。

使用大頁

如果使用者應用程式將使用 mmap 系統呼叫請求大頁,則系統管理員需要掛載 hugetlbfs 型別的檔案系統

mount -t hugetlbfs \
      -o uid=<value>,gid=<value>,mode=<value>,pagesize=<value>,size=<value>,\
      min_size=<value>,nr_inodes=<value> none /mnt/huge

此命令在目錄 /mnt/huge 上掛載 hugetlbfs 型別的(偽)檔案系統。在 /mnt/huge 上建立的任何檔案都使用大頁。

uidgid 選項設定檔案系統根目錄的所有者和組。預設情況下,採用當前程序的 uidgid

mode 選項將檔案系統根目錄的模式設定為 value & 01777。此值以八進位制給出。預設情況下,選擇值 0755。

如果平臺支援多種大頁大小,則可以使用 pagesize 選項來指定大頁大小和關聯的池。pagesize 以位元組為單位指定。如果未指定 pagesize,則將使用平臺的預設大頁大小和關聯的池。

size 選項設定該檔案系統 (/mnt/huge) 允許的最大記憶體(大頁)值。size 選項可以以位元組為單位指定,也可以指定為指定大頁池 (nr_hugepages) 的百分比。大小向下舍入到 HPAGE_SIZE 邊界。

min_size 選項設定檔案系統允許的最小記憶體(大頁)值。 min_size 可以與 size 相同的方式指定,可以是位元組數或大頁池的百分比。在掛載時,將保留 min_size 指定的大頁數量供檔案系統使用。如果沒有足夠的空閒大頁可用,則掛載將失敗。當大頁分配給檔案系統並釋放時,將調整保留計數,以便已分配和保留的大頁的總和始終至少為 min_size

nr_inodes 選項設定 /mnt/huge 可以使用的最大 inode 數。

如果命令列上未提供 sizemin_sizenr_inodes 選項,則不會設定任何限制。

對於 pagesizesizemin_sizenr_inodes 選項,可以使用 [G|g]/[M|m]/[K|k] 來表示 giga/mega/kilo。例如,size=2K 與 size=2048 具有相同的含義。

雖然在 hugetlb 檔案系統上的檔案上支援讀取系統呼叫,但不支援寫入系統呼叫。

可以使用常規的 chown、chgrp 和 chmod 命令(具有正確的許可權)來更改 hugetlbfs 上的檔案屬性。

另外,重要的是要注意,如果應用程式僅使用 shmat/shmget 系統呼叫或帶有 MAP_HUGETLB 的 mmap,則不需要這樣的掛載命令。有關如何使用帶有 MAP_HUGETLB 的 mmap 的示例,請參見下面的 map_hugetlb

希望透過共享記憶體段使用 hugetlb 記憶體的使用者應該是補充組的成員,並且系統管理員需要將該 gid 配置到 /proc/sys/vm/hugetlb_shm_group 中。相同或不同的應用程式可以使用 mmap 和 shm* 呼叫的任何組合,儘管在使用沒有 MAP_HUGETLB 的 mmap 呼叫時需要掛載檔案系統。

對 hugetlb 頁面支援的記憶體執行操作的系統呼叫的長度僅與處理器的本機頁面大小對齊;如果長度小於 hugepage 大小,它們通常會失敗並將 errno 設定為 EINVAL 或排除超出長度範圍的 hugetlb 頁面(如果未與 hugepage 對齊)。例如,如果記憶體由 hugetlb 頁面支援且長度小於 hugepage 大小,則 munmap(2) 將失敗。

示例

map_hugetlb

請參閱 tools/testing/selftests/mm/map_hugetlb.c

hugepage-shm

請參閱 tools/testing/selftests/mm/hugepage-shm.c

hugepage-mmap

請參閱 tools/testing/selftests/mm/hugepage-mmap.c

libhugetlbfs 庫提供了一系列廣泛的使用者空間工具,以幫助提高大頁的可用性、環境設定和控制。