一文讀懂 Linux 內核內存映射與頁表

一、 內存映射與頁表

1. 內存映射

我們通常所說的內存容量,指的是物理內存,只有內核纔可以直接訪問物理內存,進程並不可以。

Linux 內核給每個進程都提供了一個獨立的虛擬地址空間,並且這個地址空間是連續的。這樣,進程就可以很方便地訪問內存,更確切地說是訪問虛擬內存。

虛擬地址空間的內部又被分爲內核空間和用戶空間兩部分,不同字長(單個 CPU 指令可以處理數據的最大長度)的處理器,地址空間的範圍也不同。比如最常見的 32 位和 64 位系統:

既然每個進程都有一個這麼大的地址空間,那麼所有進程的虛擬內存加起來,自然要比實際的物理內存大得多。所以,並不是所有的虛擬內存都會分配物理內存,只有那些實際使用的虛擬內存纔會分配物理內存,並且分配後的物理內存,是通過內存映射來管理的。內存映射,其實就是將虛擬內存地址映射到物理內存地址。

2. 頁表

爲了完成內存映射,內核爲每個進程都維護了一張頁表,記錄虛擬地址與物理地址的映射關係,如下圖所示:

頁的大小隻有 4 KB ,導致的另一個問題就是,當物理內存很大時,頁表會變得非常大,佔用大量物理內存。

3. 頁表的簡單工作原理

下圖是比較簡單情況下的示意圖,用於描述在 32 位系統下,頁大小爲 4K 時,操作系統如何爲進程的虛擬地址和實際物理地址進行轉換:

  1. 目錄表,是用於索引頁表的數據結構,其中存儲着目錄項(共 1024 個、每個 4B,因此目錄表共 4B*1024=4K ),每個目錄項指向一個頁表,即可以存儲 1024 個頁表。

  2. 頁表,用來存放物理地址頁的起始地址,即頁表項(也是共 1024 個、每個 4B,因此一個頁表的大小也是 4K),由於目錄表最多可存 1024 個頁表,因此頁表的最大大小是 1024*4K=4M。

  3. 頁表項,每個頁表項指向 4K 的物理內存頁,因此頁表一共可以指向的物理內存大小爲:1024(頁表數)*1024(每個頁表的頁表項數)*4K(一個頁表項指向的物理內存大小)=4G

假如一個進程,訪問的物理內存有 1GB,即 262144 個內存頁,在 32 位系統中,頁表需要 262144*4/1024/1024=1MB,而在 64 位系統下,頁表佔用的空間增加 1 倍,即 2MB。

對於 Linux 系統中運行的 Oracle 數據庫,假如數據庫的 SGA 大小 12GB,如果一個 Oracle Process 訪問到了所有的 SGA 內存,其頁表大小會是 24MB,如果有 300 個左右的會話,那麼這 300 個連接的頁表會達到 7200MB,只不過並不是每個進程都會訪問到 SGA 中所有的內存。

頁表大小可以通過 /proc/meminfo 的 PageTables 部分查看。

爲了解決頁表項過多的問題,Linux 提供了兩種機制,也就是多級頁表和大頁(HugePage),後面我們以大頁爲重點。

二、 大頁

大頁顧名思義,就是比較大的頁,通常是 2MB。由於頁變大了,需要的頁表項也就小了,佔用物理內存也減少了。

1. 大頁的優點

2. 大頁的缺點

嚴重問題可能包括:

3. 大頁的分配方法

設置 memlock

設定 oracle 用戶可以鎖定內存的大小。這個參數在 / etc/security/limits.conf 文件,單位是 KB。開啓大頁時,這個參數很重要,如果設置過小,可能導致大頁無法被用到,白白浪費內存。

根據 What is Memlock and How to Calculate the Values for Memlock? (Doc ID 2511230.1) 文檔建議:

例如:

oracle soft memlock 18878464oracle hard memlock 18878464

重新以 oracle 用戶連接到數據庫服務器,使用 ulimit -a 命令便可看到對應設置

改爲 AUTO 方式管理 SGA

對於 11g,由於 HugePage 只能用於共享內存,不能用於 PGA,所以不能使用 AMM,只能分別設置 SGA 和 PGA。SGA 同樣只能是 AUTO 方式管理,需要將 SGA_TARGET_SIZE 設爲大於 0 的合適值。

查看建議的大頁數量

到目前爲止,大頁只能用於共享內存段等少量類型的內存。一旦將物理內存用作大頁,那麼這些物理內存就不能作其他用途,比如作爲進程的私有內存。因此不能將過多的內存設置爲大頁,通常將大頁用作 Oracle 數據庫的 SGA。

Oracle Linux: Shell Script to Calculate Values Recommended Linux HugePages / HugeTLB Configuration (Doc ID 401749.1) 提供了計算建議值的腳本。需要先設置好 SGA 等參數、啓動 Oracle、並以 Oracle 用戶執行該腳本

修改 / etc/sysctl.conf 文件,設置 vm.nr_hugepages = 建議值,執行 sysctl –p 命令

vm.nr_hugepages 這個參數值爲上步計算出的建議值。然後檢查 / proc/meminfo,如果 HugePages_Total 小於設置的數量,表明沒有足夠的連續物理內存用於這些大內存頁,需要重啓服務器。

重啓服務器和數據庫,檢查大頁使用情況

大頁是惰性分配的,用到纔會分配。隨着數據庫的使用,可以在 / proc/meminfo 中查看 HugePages_Free 是否已經減少。如果已經減少,表明已經使用到 HugePage Memory。

三、 透明大頁

在一些 Linux 系統中,transparent hugepage 被默認開啓,它允許大頁做動態的分配,而不是系統啓動後就分配好,根據 Oracle MOS DOC:1557478.1,transparent hugepage 導致了很多的問題,建議將其關閉。

1. 查看是否啓用

#未啓用應該看到[never]cat /sys/kernel/mm/transparent_hugepage/enabled

如果這個文件不存在,則檢查

#未啓用應該看到[never]cat /sys/kernel/mm/redhat_transparent_hugepage/enabled

2. 關閉透明大頁

# 重啓後失效echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag # 開機時設置never到以上文件中echo 'echo never > /sys/kernel/mm/transparent_hugepage/defrag' >> /etc/rc.d/rc.localecho 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.d/rc.local chmod +x /etc/rc.d/rc.local

SUSE Linux(區別在於開機設置 never 需要配置到的文件不同)

# 重啓後失效echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag# 開機時設置never到以上文件中echo 'echo never > /sys/kernel/mm/transparent_hugepage/defrag' >> /etc/init.d/boot.localecho 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/init.d/boot.localchmod +x /etc/init.d/boot.local

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/b3-RTIovmeRGFSi16W9CSw