Linux 操作系統原理 — 內存 — 大頁內存

目錄

前文列表

大頁內存

在頁式虛擬存儲器中,會在虛擬存儲空間和物理主存空間都分割爲一個個固定大小的頁,爲線程分配內存是也是以頁爲單位。比如:頁的大小爲 4K,那麼 4GB 存儲空間就需要 4GB/4KB=1M 條記錄,即有 100 多萬個 4KB 的頁。我們可以相待,如果頁太小了,那麼就會產生大量的頁表條目,降低了查詢速度的同時還浪費了存放頁面的主存空間;但如果頁太大了,又會容易造成浪費,原因就跟段式存儲管理方式一般。所以 Linux 操作系統默認的頁大小就是 4KB,可以通過指令查看:

$ getconf PAGE_SIZE
4096

但在某些對性能要求非常苛刻的場景中,頁面會被設置得非常的大,比如:1GB、甚至幾十 GB,這些頁被稱之爲 “大頁”(Huge Page)。大頁能夠提升性能的主要原因有以下幾點:

  • 減少頁表條目,加快檢索速度。
  • 提升 TLB 快表的命中率,TLB 一般擁有 16 ~ 128 個條目之間,也就是說當大頁爲 1GB 的時候,TLB 能夠對應 16GB ~ 128GB 之間的存儲空間。

值得注意的是,首先使用大頁的同時一般會禁止主存-輔存頁面交換(Swap),原因跟段式存儲管理方式一樣,大容量交換會讓輔存讀寫成爲 CPU 處理的瓶頸。 雖然現今在數據中心閃存化的環境中,這個問題得到了緩解,但代價就是昂貴的 SSD 存儲設備。再一個就是大頁也會使得頁內地址檢索的速度變慢,所以並非是頁面的容量越大越好,而是需要對應用程序進行大量的測試取得頁面容量與性能的曲線峯值纔對。

啓用 HugePage 的優點

  • 無需交換,不存在頁面由於內存空間不足而換入換出的問題。
  • 減輕 TLB Cache 的壓力,也就是降低了 CPU Cache 可緩存的地址映射壓力。
  • 降低 Page Table 的負載。
  • 消除 Page Table 地查找負載。
  • 提高內存的整體性能。

啓用 HugePage 的缺點

  • HugePages 會在系統啓動時,直接分配並保留對應大小的內存區域
  • HugePages 在開機之後,如果沒有管理員的介入,是不會釋放和改變的。

Linux 的大頁內存

在 Linux 中,物理內存是以頁爲單位來管理的。默認的,頁的大小爲 4KB。 1MB 的內存能劃分爲 256 頁; 1GB 則等同於 256000 頁。 CPU 中有一個內置的內存管理單元(MMU),用於存儲這些頁的列表(頁表),每頁都有一個對應的入口地址。4KB 大小的頁面在 “分頁機制” 提出的時候是合理的,因爲當時的內存大小不過幾十兆字節。然而,當前計算機的物理內存容量已經增長到 GB 甚至 TB 級別了,操作系統仍然以 4KB 大小爲頁面的基本單位的話,會導致 CPU 中 MMU 的頁面空間不足以存放所有的地址條目,則會造成內存的浪費。

同時,在 Linux 操作系統上運行內存需求量較大的應用程序時,採用的默認的 4KB 頁面,將會產生較多 TLB Miss 和缺頁中斷,從而大大影響應用程序的性能。當操作系統以 2MB 甚至更大作爲分頁的單位時,將會大大減少 TLB Miss 和缺頁中斷的數量,顯著提高應用程序的性能。

爲了解決上述問題,自 Linux Kernel 2.6 起,引入了 Huge pages(巨型頁)的概念,目的是通過使用大頁內存來取代傳統的 4KB 內存頁面, 以適應越來越大的內存空間。Huge pages 有 2MB 和 1GB 兩種規格,2MB 大小(默認)適合用於 GB 級別的內存,而 1GB 大小適合用於 TB 級別的內存。

大頁的實現原理

爲了能以最小的代價實現大頁面支持,Linux 採用了 hugetlb 和 hugetlbfs 兩個概念。其中,hugetlb 是記錄在 TLB 中的條目並指向 hugepages,而 hugetlbfs 則是一個特殊文件系統(本質是內存文件系統)。hugetlbfs 主要的作用是使得應用程序可以根據需要靈活地選擇虛擬存儲器頁面的大小,而不會全局性的強制使用某個大小的頁面。在 TLB 中通過 hugetlb 來指向 hugepages,可以通過 hugetlb entries 來調用 hugepages,這些被分配的 hugepages 再以 hugetlbfs 內存文件系統的形式提供給進程使用

  • Regular Page 的分配:當一個進程請求內存時,它需要訪問 PageTable 去調用一個實際的物理內存地址,繼而獲得內存空間。
    在這裏插入圖片描述
  • Huge Page 的分配:當系統配置 Huge pages 後,進程依然通過普通的 PageTable 來獲取到實際的物理內存地址。但不同的是,在 Process PageTable 和 System PageTable 第增加了 Hugepage(HPage)屬性。
    在這裏插入圖片描述
    可見,進程當需要使用 Huge pages 時,只需要聲明 Hugepage 屬性,讓系統分配 PageTable 中的 Huge pages 條目即可實現。所以,實際上 Regular page 和 Huge page 是共享一個 PageTable 的,這就是所謂的以最小的代碼來支持 Huge pages。

使用 Huge pages 的好處是很明顯的,假設應用程序需要 2MB 的內存,如果操作系統以 4KB 作爲分頁的單位,則需要 512 個頁面,進而在 TLB 中需要 512 個表項,同時也需要 512 個頁表項,操作系統需要經歷至少 512 次 TLB Miss 和 512 次缺頁中斷才能將 2MB 應用程序空間全部映射到物理內存;然而,當操作系統採用 2MB 作爲分頁的基本單位時,只需要一次 TLB Miss 和一次缺頁中斷,就可以爲 2MB 的應用程序空間建立虛實映射,並在運行過程中無需再經歷 TLB Miss 和缺頁中斷(假設未發生 TLB 項替換和 Swap)。

此外,使用 Huge pages 還能減少系統管理和處理器訪問內存頁的時間(擴大了 TLB 快頁表查詢的內存地址範圍),Linux 內核中的 Swap(內存交換)守護進程也不會管理大頁面佔用的這部分空間。合理設置大頁面能減少內存操作的負擔,減少訪問頁表造成性能瓶頸的可能性,從而提升系統性能。由此,如果你的系統經常碰到因爲 Swap 而引發的性能問題,或者你的計算機內存空間非常大時,都可以考慮啓用大頁內存。

大頁內存配置

大頁面配置需要連續的內存空間,因此在開機時就分配是最可靠的設置方式。配置大頁面的參數有:

  • hugepages :在內核中定義了開機啓動時就分配的永久大頁面的數量。默認爲 0,即不分配。只有當系統有足夠的連續可用頁時,分配纔會成功。由該參數保留的頁不能用於其他用途。

  • hugepagesz: 在內核中定義了開機啓動時分配的大頁面的大小。可選值爲 2MB 和 1GB 。默認是 2MB 。

  • default_hugepagesz:在內核中定義了開機啓動時分配的大頁面的默認大小。

  • Step 1. 查看 Linux 操作系統是否啓動了大頁內存,如果 HugePages_Total 爲 0,意味着 Linux 沒有設置或沒有啓用 Huge pages。

$ grep -i HugePages_Total /proc/meminfo
HugePages_Total:       0
  • Step 2. 查看是否掛載了 hugetlbfs
$ mount | grep hugetlbfs
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime)
  • Step 3. 如果沒有掛載則手動掛載
$ mkdir /mnt/huge_1GB
$ mount -t hugetlbfs nodev /mnt/huge_1GB

$ vim /etc/fstab
nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0
  • Step 4. 修改 grub2,例如爲系統配置 10 個 1GB 的大頁面
$ vim /etc/grub2.cfg
# 定位到 linux16 /vmlinuz-3.10.0-327.el7.x86_64 在行末追加
default_hugepagesz=1G hugepagesz=1G hugepages=10

NOTE:配置大頁面後,系統在開機啓動時會首選嘗試在內存中找到並預留連續的大小爲 hugepages * hugepagesz 的內存空間。如果內存空間不滿足,則啓動會報錯 Kernel Panic, Out of Memory 等錯誤。

  • Step 5. 重啓系統,查看更詳細的大頁內存信息
$ cat /proc/meminfo | grep -i Huge
AnonHugePages:   1433600 kB     # 匿名 HugePages 數量
HugePages_Total:       0        # 分配的大頁面數量
HugePages_Free:        0        # 沒有被使用過的大頁面數量
HugePages_Rsvd:        0        # 已經被分配預留但是還沒有使用的大頁面數目,應該儘量保持 HugePages_Free - HugePages_Rsvd = 0
HugePages_Surp:        0        # surplus 的縮寫,表示大頁內存池中大於 /proc/sys/vm/nr_hugepages 中值的大頁面數量
Hugepagesize:       1048576 kB     # 每個大頁面的 Size,與 HugePages_Total 的相乘得到大頁面池的總容量

如果大頁面的 Size 一致,則可以通過 /proc/meminfo 中的 HugepagesizeHugePages_Total 計算出大頁面所佔內存空間的大小。這部分空間會被算到已用的內存空間裏,即使還未真正被使用。因此,用戶可能觀察到下面現象:使用 free 命令查看已用內存很大,但 top 或者 ps 中看到 %mem 的使用總量加起來卻很少。

  • Step 6. 如果上述輸出看見 Hugepagesize 已經設置成 1GB,但 HugePages_Total 還是爲 0,那麼需要修改內核參數設定大頁面的數量
$ sysctl -w vm.nr_hugepages=10

# 或者
$ echo 'vm.nr_hugepages = 10' > /etc/sysctl.conf 
$ sysctl -p

NOTE:一般情況下,配置的大頁面可能主要供特定的應用程序或服務使用,其他進程是無法共享這部分空間的(如 Oracle SGA)。 請根據系統物理內存和應用需求來設置合適的大小,避免大頁面使用的浪費;以及造成其他進程因競爭剩餘可用內存而出現內存溢出的錯誤,進而導致系統崩潰的現象。默認的,當存在大頁面時,會在應用進程或者內核進程申請大頁內存的時候,優先爲它們分配大頁面,大頁面不足以分配時,纔會分配傳統的 4KB 頁面。查看哪個程序在使用大頁內存:

grep -e AnonHugePages /proc/*/smaps | awk '{if(2>4)print0}' | awk -F "/" '{print0;system("ps−fp"3)}'

透明巨型頁 THP

Transparent Huge pages(THP,透明大頁) 自 RHEL 6 開始引入。由於傳統的 Huge pages 很難手動的管理,對於程序而言,可能需要修改很多的代碼纔能有效的使用。THP 的引入就是爲了便於系統管理員和開發人員使用大頁內存。THP 是一個抽象層,能夠自動創建、管理和使用傳統大頁。操作系統將大頁內存看作是一種系統資源,在 THP 開啓的情況下,其他的進程也可以申請和釋放大頁內存。

Huge pages 和 Transparent Huge pages 在大頁內存的使用方式上存在區別,前者是預分配的方式,而後者則是動態分配的方式,顯然後者更適合程序使用。需要注意的是,THP 雖然方便,但在某些場景種仍然會建議我們關閉,這個需要結合實際應用場景慎重考慮。

手動關閉 THP

$ echo never > /sys/kernel/mm/transparent_hugepage/enabled 
$ echo never > /sys/kernel/mm/transparent_hugepage/defrag

$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

# - [always] 表示啓用了 THP
# - [never] 表示禁用了 THP
# - [madvise] 表示只在 MADV_HUGEPAGE 標誌的 VMA 中使用 THP

永久關閉 THP

vim /etc/grub2.cfg

# 在 cmdline 追加:
transparent_hugepage=never

大頁面對內存的影響

需要注意的是,爲大頁面分配的內存空間會被計算到已用內存空間中,即使它們還未真正被使用。因此,你可能觀察到下面現象:使用 free 命令查看已用內存很大,但 top 或者 ps 指令中看到 %MEM 的使用總量加起來卻很少。

例如:總內存爲 32G,並且分配了 12G 大頁面的 free 如下

[root@localhost ~]# free -g
              total        used        free      shared  buff/cache   available
Mem:             31          16          14           0           0          14
Swap:             3           0           3

命令 top 輸出, Shift+m 按內存使用量進行排序:
在這裏插入圖片描述
命令 ps -eo uid,pid,rss,trs,pmem,stat,cmd,查看進程的內存使用量:
在這裏插入圖片描述
這種情況就導致了一個問題,如果盲目的去提高大頁內存空間的佔比,就很可能會出現胖的胖死,餓的餓死的問題。導致大頁內存空間的浪費,因爲普通程序是未必能夠使用大頁內存的

大頁內存的性能問題

在頁式虛擬存儲器中,會在虛擬存儲空間和物理主存空間都分割爲一個個固定大小的頁,爲線程分配內存是也是以頁爲單位。比如:頁的大小爲 4K,那麼 4GB 存儲空間就需要 4GB/4KB=1M 條記錄,即有 100 多萬個 4KB 的頁。我們可以相待,如果頁太小了,那麼就會產生大量的頁表條目,降低了查詢速度的同時還浪費了存放頁面的主存空間;但如果頁太大了,又會容易造成浪費,原因就跟段式存儲管理方式一般。所以 Linux 操作系統默認的頁大小就是 4KB,可以通過指令查看:

$ getconf PAGE_SIZE
4096

但在某些對性能要求非常苛刻的場景中,頁面會被設置得非常的大,比如:1GB、甚至幾十 GB,這些頁被稱之爲 “大頁”(Huge Page)。大頁能夠提升性能的主要原因有以下幾點:

  • 減少頁表條目,加快檢索速度。
  • 提升 TLB 快表的命中率,TLB 一般擁有 16 ~ 128 個條目之間,也就是說當大頁爲 1GB 的時候,TLB 能夠對應 16GB ~ 128GB 之間的存儲空間。

值得注意的是,首先使用大頁的同時一般會禁止主存-輔存頁面交換,原因跟段式存儲管理方式一樣,大容量交換會讓輔存讀寫成爲 CPU 處理的瓶頸。再一個就是大頁也會使得頁內地址檢索的速度變慢,所以並非是頁面的容量越大越好,而是需要對應用程序進行大量的測試取得頁面容量與性能的曲線峯值纔對。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章