偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

認(rèn)識(shí) Linux 內(nèi)存構(gòu)成:Linux 內(nèi)存調(diào)優(yōu)之頁(yè)表、TLB、缺頁(yè)異常、大頁(yè)認(rèn)知

系統(tǒng) Linux
當(dāng)啟動(dòng)一個(gè)程序時(shí),會(huì)先給程序分配合適的虛擬地址空間,但是不需要把所有虛擬地址空間都映射到物理內(nèi)存,而是把程序在運(yùn)行中需要的數(shù)據(jù),映射到物理內(nèi)存,需要時(shí)可以再動(dòng)態(tài)映射分配物理內(nèi)存因?yàn)槊總€(gè)進(jìn)程都維護(hù)著自己的虛擬地址空間,每個(gè)進(jìn)程都有一個(gè)頁(yè)表來(lái)定位虛擬內(nèi)存到物理內(nèi)存的映射,每個(gè)虛擬內(nèi)存也在表中都有一個(gè)對(duì)應(yīng)的條目。

認(rèn)識(shí) Linux 內(nèi)存構(gòu)成:Linux 內(nèi)存調(diào)優(yōu)之頁(yè)表、TLB、大頁(yè)認(rèn)知

當(dāng)啟動(dòng)一個(gè)程序時(shí),會(huì)先給程序分配合適的虛擬地址空間,但是不需要把所有虛擬地址空間都映射到物理內(nèi)存,而是把程序在運(yùn)行中需要的數(shù)據(jù),映射到物理內(nèi)存,需要時(shí)可以再動(dòng)態(tài)映射分配物理內(nèi)存

因?yàn)槊總€(gè)進(jìn)程都維護(hù)著自己的虛擬地址空間,每個(gè)進(jìn)程都有一個(gè)頁(yè)表來(lái)定位虛擬內(nèi)存到物理內(nèi)存的映射,每個(gè)虛擬內(nèi)存也在表中都有一個(gè)對(duì)應(yīng)的條目。

這里的頁(yè)表是進(jìn)程用于跟蹤虛擬內(nèi)存到物理內(nèi)存的映射,那么實(shí)際的數(shù)據(jù)結(jié)構(gòu)是什么的?

頁(yè)表

如果每個(gè)進(jìn)程都分配一個(gè)大的頁(yè)表,64位系統(tǒng) 理論虛擬地址空間為2^64字節(jié),但實(shí)際 Linux 系統(tǒng)通常采用48位有效虛擬地址

┌──[root@liruilongs.github.io]-[~]
└─$cat /proc/cpuinfo | grep address
address sizes   : 45 bits physical, 48 bits virtual
address sizes   : 45 bits physical, 48 bits virtual
┌──[root@liruilongs.github.io]-[~]
└─$

即2 ^48字節(jié)(256TB)。若頁(yè)面大小為4KB(2^12字節(jié)),則需管理的頁(yè)表項(xiàng)數(shù)量為 虛擬頁(yè)數(shù) = 2^48 / 2^12 = 2^36

每個(gè)頁(yè)表項(xiàng)需要存儲(chǔ)物理頁(yè)幀號(hào)(PFN)和權(quán)限標(biāo)志,通常占用8字節(jié)。所以頁(yè)表的總內(nèi)存需求為: 總大小 = 2^36 × 8 = 2^39 字節(jié) = 512GB

512G ,即一個(gè)進(jìn)程的頁(yè)表本身就是巨大的,如果多個(gè)進(jìn)程更夸張,但是實(shí)際中進(jìn)程僅使用少量?jī)?nèi)存(如1GB),可能只需要幾個(gè)映射,單級(jí)頁(yè)表仍需預(yù)分配全部虛擬地址空間對(duì)應(yīng)的頁(yè)表項(xiàng),造成大部分的空間浪費(fèi),況且也沒(méi)有那么多內(nèi)存存放頁(yè)表。

多級(jí)頁(yè)表

這里優(yōu)化的方案就是將頁(yè)面分級(jí)管理(多級(jí)頁(yè)表 Multi-Level Page Table),將一個(gè)大頁(yè)表大小分成很多小表,最終指向頁(yè)表?xiàng)l目(一條映射記錄),系統(tǒng)只需要給進(jìn)程分配頁(yè)表目錄,從而降低映射總表的大小。

這里怎么理解多級(jí)頁(yè)表和頁(yè)表目錄?

想象你要管理一個(gè)超大的圖書(shū)館(相當(dāng)于虛擬地址空間),里面有 幾百萬(wàn)本書(shū)(相當(dāng)于內(nèi)存頁(yè))。如果用一個(gè)超大的總目錄(只有小標(biāo)題)記錄每本書(shū)的位置,這個(gè)目錄本身就會(huì)占據(jù)整個(gè)圖書(shū)館的空間,顯然不現(xiàn)實(shí)。多級(jí)頁(yè)表就像是對(duì)只有小標(biāo)題的目錄作了多級(jí)目錄劃分。

  • 第一層目錄(PGD):記錄整個(gè)圖書(shū)館分為 512個(gè)大區(qū)(每個(gè)大區(qū)對(duì)應(yīng)9位索引,2?=512)。
  • 第二層目錄(PUD):每個(gè)大區(qū)再分為 512個(gè)小區(qū)。
  • 第三層目錄(PMD):每個(gè)小區(qū)再分 512個(gè)書(shū)架。
  • 第四層目錄(PTE):每個(gè)書(shū)架對(duì)應(yīng) 512本書(shū)(每本書(shū)即4KB內(nèi)存頁(yè))

我們知道數(shù)組存儲(chǔ)只存儲(chǔ)首地址,之后的元素會(huì)根據(jù)首地址計(jì)算,這里的頁(yè)表目錄類似首地址,所以可以通過(guò)多級(jí)目錄位置直接定位映射記錄。

現(xiàn)代系統(tǒng)多使用上面多級(jí)頁(yè)表(如 x86-64 的 四級(jí)頁(yè)表)的方式,逐步縮小搜索范圍,但是多級(jí)頁(yè)表也有一定的弊端,后面我們會(huì)討論

首先會(huì)按照上面的方式對(duì) 48位虛擬地址進(jìn)行拆分,虛擬地址被分割為多個(gè)索引字段,每一級(jí)索引對(duì)應(yīng)一級(jí)頁(yè)表,逐級(jí)查詢頁(yè)表項(xiàng)(PTE),48 位虛擬地址可能拆分為:

PGD索引(9位) → PUD索引(9位) → PMD索引(9位) → PTE索引(9位) → 頁(yè)內(nèi)偏移(12位)

每級(jí)頁(yè)表僅需512(2^9)項(xiàng)(9位索引),每個(gè)表項(xiàng)是 8 字節(jié),所以單級(jí)占用4KB,而且僅在實(shí)際需要時(shí)分配下級(jí)頁(yè)表。

當(dāng)進(jìn)程需要映射1GB內(nèi)存時(shí),只需要分配必要的頁(yè)表僅需

總頁(yè)表大小 = 1(PGD)+1(PUD)+1(PMD)+512(PTE) = 515×4KB ≈ 2.02MB

PGD、PUD、PMD各一個(gè),PTE需要512個(gè),總共515個(gè)頁(yè)表項(xiàng),每個(gè)4KB,總共約2.02MB。

那么這里的 512個(gè)索引頁(yè)面是如何計(jì)算的?

1GB/4KB=262,144個(gè)頁(yè)面, 262,144/512=512 個(gè)PTE索引頁(yè)(一個(gè)索引頁(yè)存放512頁(yè)表項(xiàng)),一個(gè)頁(yè)表項(xiàng)對(duì)應(yīng)一個(gè)內(nèi)存頁(yè)

前面我們也有講過(guò),在具體的分配上,內(nèi)核空間位于虛擬地址的高位(高24位),用戶態(tài)內(nèi)存空間位于虛擬地址低位,頁(yè)表本身存儲(chǔ)在內(nèi)核空間,用戶程序無(wú)法直接修改,僅能通過(guò)系統(tǒng)調(diào)用請(qǐng)求內(nèi)核操作。用戶態(tài)程序申請(qǐng)內(nèi)存時(shí),內(nèi)核僅分配虛擬地址,實(shí)際物理頁(yè)的分配由缺頁(yè)異常觸發(fā)。此時(shí)內(nèi)核介入,更新頁(yè)表項(xiàng)并映射物理頁(yè),這一過(guò)程需切換到內(nèi)核態(tài)執(zhí)行。

那里這里的缺頁(yè)異常又是什么?

缺頁(yè)異常

當(dāng)進(jìn)程訪問(wèn)系統(tǒng)沒(méi)有映射物理頁(yè)的虛擬內(nèi)存頁(yè)時(shí),內(nèi)核就會(huì)產(chǎn)生一個(gè) page fault 異常事件。

minor fualt

當(dāng)進(jìn)程缺頁(yè)事件發(fā)生在第一次訪問(wèn)虛擬內(nèi)存時(shí),虛擬內(nèi)存已分配但未映射(如首次訪問(wèn)、寫(xiě)時(shí)復(fù)制、共享內(nèi)存同步)物理地址,內(nèi)核會(huì)產(chǎn)生一個(gè) minor page fualt,并分配新的物理內(nèi)存頁(yè)。minor page fault 產(chǎn)生的開(kāi)銷比較小。

minor page fualt 典型場(chǎng)景:

  • 首次訪問(wèn):進(jìn)程申請(qǐng)內(nèi)存后,內(nèi)核延遲分配物理頁(yè)(Demand Paging),首次訪問(wèn)時(shí)觸發(fā)。
  • 寫(xiě)時(shí)復(fù)制(COW):fork()創(chuàng)建子進(jìn)程時(shí)共享父進(jìn)程內(nèi)存,子進(jìn)程寫(xiě)操作前觸發(fā)
  • 共享庫(kù)加載:動(dòng)態(tài)鏈接庫(kù)被多個(gè)進(jìn)程共享,首次加載到物理內(nèi)存時(shí)觸發(fā),即會(huì)共享頁(yè)表

major fault

當(dāng)物理頁(yè)未分配且需從磁盤(pán)(Swap分區(qū)或文件)加載數(shù)據(jù),內(nèi)核就會(huì)產(chǎn)生一個(gè) majorpage fault,比如內(nèi)核通過(guò)Swap分區(qū),將內(nèi)存中的數(shù)據(jù)交換出去放到了硬盤(pán),需要時(shí)從硬盤(pán)中重新加載程序或庫(kù)文件的代碼到內(nèi)存。涉及到磁盤(pán)I/O,因此一個(gè)major fault對(duì)性能影響比較大,典型場(chǎng)景有

  • Swap In:物理內(nèi)存不足時(shí),內(nèi)核將內(nèi)存頁(yè)換出到 Swap 分區(qū),再次訪問(wèn)需換回。
  • 文件映射(mmap):通過(guò) mmap 映射文件到內(nèi)存,首次訪問(wèn)文件內(nèi)容需從磁盤(pán)讀取。

Minor Fault 是內(nèi)存層面的輕量級(jí)操作,Major Fault 是涉及磁盤(pán)I/O的重型操作。頻繁的 Major Fault 就需要考慮性能問(wèn)題, 對(duì)于缺頁(yè)異常,我們通過(guò) ps、vmstat、perf等工具定位性能瓶頸

通過(guò) ps 命令查看當(dāng)前系統(tǒng)存在缺頁(yè)異常的進(jìn)程的排序

┌──[root@liruilongs.github.io]-[~]
└─$ps -eo pid,minflt,majflt,comm | awk '$2 > 0 && $3 > 0 {print}'
    PID MINFLT MAJFLT COMMAND
      1  55646    189 systemd
    704    959      7 systemd-journal
    719   1912      2 systemd-udevd
    892     80      3 auditd
    913    553     12 dbus-broker-lau
    915    281      4 dbus-broker
    918  15617    206 firewalld
    919    325      6 irqbalance
    921    740      5 systemd-logind
    925    166      5 chronyd
    955   1243    100 NetworkManager
    991  26090    281 /usr/sbin/httpd
    998   2683    260 php-fpm
    999    923     17 sshd
   1002   9775      7 tuned
   1006    862      3 crond
   1121   6976    225 mariadbd
   1150   2060    125 polkitd
   1213    731     24 rsyslogd
   1498    390      7 pmcd
   1518    516     11 pmdaroot
   1535    470      4 pmdaproc
   1544    410      2 pmdaxfs
   1551    447      4 pmdalinux
   1558    409      2 pmdakvm
   1872   2109      1 /usr/sbin/httpd
   1874   3701      9 /usr/sbin/httpd
   2201   2654      2 bash
   2245    678      6 sudo
   2246   3300      1 bash
   4085    541     10 htop
┌──[root@liruilongs.github.io]-[~]
└─$

也可以通過(guò) perf stat 來(lái)查看指定命令,進(jìn)程的 缺頁(yè)異常情況

┌──[root@liruilongs.github.io]-[~]
└─$perfstat -e minor-faults,major-faults hostnamectl
 Static hostname: liruilongs.github.io
       Icon name: computer-vm
         Chassis: vm ?
      Machine ID: 7deac2815b304f9795f9e0a8b0ae7765
         Boot ID: 5041b68a4d574df2b59289e33e85bdd5
  Virtualization: vmware
Operating System: Rocky Linux 9.4 (Blue Onyx)
     CPE OS Name: cpe:/o:rocky:rocky:9::baseos
          Kernel: Linux 5.14.0-427.20.1.el9_4.x86_64
    Architecture: x86-64
 Hardware Vendor: VMware, Inc.
  Hardware Model: VMware Virtual Platform
Firmware Version: 6.00

 Performance counter stats for'hostnamectl':

               463      minor-faults
                 0      major-faults

       0.132397887 seconds time elapsed

       0.009642000 seconds user
       0.004471000 seconds sys


┌──[root@liruilongs.github.io]-[~]
└─$

可以看到 hostnamectl 命令因內(nèi)存動(dòng)態(tài)分配觸發(fā)了 463 次次缺頁(yè)中斷,下面是一些常見(jiàn)的對(duì)應(yīng)缺頁(yè)異常的調(diào)優(yōu)建議

減少 Major Fault:

  • 增加或者禁用物理內(nèi)存:避免頻繁 Swap。
  • 調(diào)整 Swappiness:降低內(nèi)核參數(shù) /proc/sys/vm/swappiness,減少內(nèi)存換出傾向。
  • 預(yù)加載數(shù)據(jù):使用 mlock() 鎖定關(guān)鍵內(nèi)存頁(yè)(如實(shí)時(shí)系統(tǒng)),禁止換出。
  • 優(yōu)化文件訪問(wèn):對(duì) mmap 文件進(jìn)行順序讀取或預(yù)讀(posix_fadvise)。

降低 Minor Fault:

  • 預(yù)分配內(nèi)存:避免 Demand Paging 的延遲(如啟動(dòng)時(shí)初始化全部?jī)?nèi)存)。
  • 減少 COW 開(kāi)銷:避免頻繁 fork(),改用 posix_spawn 或線程。

通過(guò)多級(jí)頁(yè)表的方式極大的縮小和頁(yè)表空間,可以按需分配,但是多級(jí)頁(yè)表也有一定的局限性,一是地址轉(zhuǎn)換復(fù)雜度,層級(jí)增加會(huì)降低轉(zhuǎn)換效率,需依賴硬件加速(如MMU的并行查詢能力)。二是內(nèi)存碎片風(fēng)險(xiǎn),子表的離散分配可能導(dǎo)致物理內(nèi)存碎片化(內(nèi)存不連續(xù)+頻繁的回收創(chuàng)建),需操作系統(tǒng)優(yōu)化分配策略

為了解決多級(jí)頁(yè)表的地址轉(zhuǎn)換需多次訪存(如四級(jí)頁(yè)表需4次內(nèi)存訪問(wèn)),導(dǎo)致延遲增加,常見(jiàn)的解決方案包括:

  • TLB(快表)緩存:存儲(chǔ)最近使用的頁(yè)表項(xiàng),命中時(shí)直接獲取物理地址,減少訪存次數(shù)
  • 巨型頁(yè)(Huge Page):使用2MB或1GB的頁(yè)面粒度,減少頁(yè)表層級(jí)和項(xiàng)數(shù)(大頁(yè)的使用需要操作系統(tǒng)和應(yīng)用程序的支持)

所以進(jìn)程通過(guò)頁(yè)表查詢虛擬地址和物理地址的映射關(guān)系, 首先會(huì)檢查  TLB(Translation Lookaside Buffer)高速緩存頁(yè)表項(xiàng),CPU硬件緩存.

那么這里的 TLB 是如何參與到到內(nèi)存映射的?

TLB

TLB 是內(nèi)存管理單元(MMU)的一部分,本質(zhì)是頁(yè)表的高速緩存,存儲(chǔ)最近被頻繁訪問(wèn)的頁(yè)表項(xiàng)(虛擬地址到物理地址的映射關(guān)系)的副本,是集成在 CPU 內(nèi)部的 高速緩存硬件,用于加速虛擬地址到物理地址轉(zhuǎn)換的專用緩存,通過(guò)專用電路實(shí)現(xiàn)高速地址轉(zhuǎn)換,與數(shù)據(jù)緩存(Data Cache)和指令緩存(Instruction Cache)并列,共同構(gòu)成 CPU 緩存體系

上面我們講當(dāng)進(jìn)程查詢分層頁(yè)面的映射信息會(huì)導(dǎo)致延遲增加。因此,當(dāng)缺頁(yè)異常觸發(fā)內(nèi)核分配物理內(nèi)存將從虛擬地址到物理地址的映射添加到頁(yè)表中時(shí),它還將該映射條目緩存在 TLB 硬件緩存中,通過(guò)緩存進(jìn)程最近使用的頁(yè)映射來(lái)加速地址轉(zhuǎn)換。

當(dāng)下一次查詢發(fā)生的時(shí)候,首先會(huì)在 TLB  中查詢是否有緩存,如果有的話會(huì)直接獲取,沒(méi)有的話,走上面缺頁(yè)異常的流程

進(jìn)程訪問(wèn)虛擬地址 → MMU 查詢 TLB → [命中 → 直接獲取物理地址]
                │
                └→ [未命中 → 查詢頁(yè)表 → 權(quán)限檢查 → 缺頁(yè)處理(可選)→ 生成物理地址 → 更新 TLB]
                │
                └→ 訪問(wèn)物理內(nèi)存

所以 TLB 命中率直接影響程序效率。若 TLB 未命中(Miss),需通過(guò)頁(yè)表遍歷獲取物理地址,導(dǎo)致額外延遲(通常是 TLB 命中時(shí)間的數(shù)十倍),從內(nèi)存加載頁(yè)表項(xiàng),并更新TLB緩存(可能觸發(fā)條目替換,如LRU算法)

可以通過(guò) perf stat 命令來(lái)查看某一個(gè)命令或者進(jìn)程的 TLB 命中情況

┌──[root@liruilongs.github.io]-[~]
└─$perfstat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses hostnamectl
 Static hostname: liruilongs.github.io
       Icon name: computer-vm
         Chassis: vm ?
      Machine ID: 7deac2815b304f9795f9e0a8b0ae7765
         Boot ID: 5041b68a4d574df2b59289e33e85bdd5
  Virtualization: vmware
Operating System: Rocky Linux 9.4 (Blue Onyx)
     CPE OS Name: cpe:/o:rocky:rocky:9::baseos
          Kernel: Linux 5.14.0-427.20.1.el9_4.x86_64
    Architecture: x86-64
 Hardware Vendor: VMware, Inc.
  Hardware Model: VMware Virtual Platform
Firmware Version: 6.00

 Performance counter stats for'hostnamectl':

                 0      dTLB-loads
                 0      dTLB-load-misses
   <not supported>      iTLB-loads
                 0      iTLB-load-misses

       0.131288737 seconds time elapsed

       0.010681000 seconds user
       0.005377000 seconds sys


┌──[root@liruilongs.github.io]-[~]
  • dTLB-loads 表示數(shù)據(jù)地址轉(zhuǎn)換(Data TLB)的加載次數(shù),
  • dTLB-load-misses 表示未命中次數(shù)
  • iTLB-loads 表示指令地址轉(zhuǎn)換(Instruction TLB)的加載次數(shù)
  • iTLB-load-misses 指令 TLB 未命中次數(shù)為 0,說(shuō)明所有指令訪問(wèn)均命中 TLB 緩存。

查看指定進(jìn)程的命中情況使用 -p <pid> 的方式

┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses -p 1
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$

大頁(yè)(巨型頁(yè))

大頁(yè)是另一種解決多級(jí)頁(yè)表多次訪問(wèn)內(nèi)存的手段,顧名思義,傳統(tǒng)的內(nèi)存頁(yè)是 4KB,大于 4KB 的內(nèi)存頁(yè)被稱為大頁(yè),通過(guò)大頁(yè)可以降低多級(jí)頁(yè)表的層級(jí)

同時(shí) TLB 也有一定的局限性,存儲(chǔ)條目是固定的,當(dāng)進(jìn)程需要訪問(wèn)大量?jī)?nèi)存的時(shí)候,比如數(shù)據(jù)庫(kù)應(yīng)用,將會(huì)導(dǎo)致大量 TLB 未命中而影響性能,還是需要通過(guò)多級(jí)頁(yè)表來(lái)轉(zhuǎn)化地址,所以除了 4KB 頁(yè)面之外,Linux 內(nèi)核還通過(guò)大頁(yè)面機(jī)制支持大容量?jī)?nèi)存頁(yè)面。

通過(guò)查看 /proc/meminfo 文件確定具體系統(tǒng)的大頁(yè)大小以及使用情況,大頁(yè)分為 標(biāo)準(zhǔn)大頁(yè)(靜態(tài)大頁(yè))和透明大頁(yè)

靜態(tài)大頁(yè)其核心原理是通過(guò)增大內(nèi)存頁(yè)的尺寸(如2MB或1GB),優(yōu)化虛擬地址到物理地址的轉(zhuǎn)換效率,從而提升系統(tǒng)性能。x86 64 位架構(gòu)支持多種大頁(yè)規(guī)格,比如 4KiB,2MiB 以及 1GiB。Linux 系統(tǒng)默認(rèn)是 2MiB

需要說(shuō)明的是,大頁(yè)配置僅受用語(yǔ)支持大頁(yè)的應(yīng)用程序,對(duì)于不支持大頁(yè)的應(yīng)用程序來(lái)說(shuō)是無(wú)效的,同時(shí)大頁(yè)會(huì)導(dǎo)致內(nèi)存剩余空間變小 后面我們會(huì)介紹幾個(gè)Demo

透明大頁(yè)用于合并傳統(tǒng)內(nèi)存頁(yè)。

┌──[root@liruilongs.github.io]-[~]
└─$cat /proc/meminfo | grep Hug
AnonHugePages:    165888 kB
ShmemHugePages:        0 kB
FileHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB
┌──[root@liruilongs.github.io]-[~]
└─$

Hugepagesize: 2048 kB: 靜態(tài)大頁(yè)的默認(rèn)大小為 2MB,這里的大頁(yè)是標(biāo)準(zhǔn)大頁(yè),若需使用 1GB 大頁(yè),需修改內(nèi)核參數(shù)配置,前提是需要CPU 支持才行

AnonHugePages:    165888 kB:  透明大頁(yè)(Transparent HugePages) 匿名頁(yè)占用的內(nèi)存總量為 165,888 KB(約 162 MB)

大部分部署數(shù)據(jù)庫(kù)的機(jī)器會(huì)禁用透明大頁(yè)?這是什么原因

透明大頁(yè)

透明大頁(yè)(Transparent Huge Pages,THP)是內(nèi)核提供的一種動(dòng)態(tài)內(nèi)存管理機(jī)制,它通過(guò)自動(dòng)將多個(gè) 4KB 小頁(yè) 合并為 2MB 或 1GB 大頁(yè),減少頁(yè)表項(xiàng)數(shù)量并提升 TLB(地址轉(zhuǎn)換緩存)命中率,從而優(yōu)化內(nèi)存訪問(wèn)性能

與需手動(dòng)預(yù)分配的靜態(tài)大頁(yè)(HugeTLB)不同,THP 對(duì)應(yīng)用程序透明且無(wú)需配置,適用于順序內(nèi)存訪問(wèn)(如大數(shù)據(jù)處理)和低實(shí)時(shí)性場(chǎng)景。但動(dòng)態(tài)合并可能引發(fā) 內(nèi)存碎片 和 性能抖動(dòng),因此對(duì)延遲敏感的數(shù)據(jù)庫(kù)(如 MySQL)或高并發(fā)系統(tǒng)建議關(guān)閉 THP

下面為透明大頁(yè)相關(guān)配置

┌──[root@liruilongs.github.io]-[~] 
└─$ls /sys/kernel/mm/transparent_hugepage/
defrag          enabled         hpage_pmd_size  khugepaged/     shmem_enabled   use_zero_page

enabled 用于配置是否開(kāi)啟 THP

┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
┌──[root@liruilongs.github.io]-[~]
└─$cat /proc/meminfo | grep Anon
AnonPages:        356692 kB
AnonHugePages:    167936 kB
┌──[root@liruilongs.github.io]-[~]
└─$

禁用透明大頁(yè) 某些場(chǎng)景(如數(shù)據(jù)庫(kù))建議禁用 THP 以穩(wěn)定性能:

# 臨時(shí)禁用
echo never > /sys/kernel/mm/transparent_hugepage/enabled

使用 grubby 更新內(nèi)核啟動(dòng)參數(shù),grubby 用于 動(dòng)態(tài)修改內(nèi)核啟動(dòng)參數(shù) 或 設(shè)置默認(rèn)內(nèi)核,無(wú)需手動(dòng)編輯配置文件

# 永久禁用
┌──[root@liruilongs.github.io]-[~] 
└─$grubby --update-kernel=ALL --args="transparent_hugepage=never"
┌──[root@liruilongs.github.io]-[~] 
└─$reboot

確認(rèn)配置

┌──[root@liruilongs.github.io]-[~] 
└─$cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]
┌──[root@liruilongs.github.io]-[~] 
└─$

再次查看透明大頁(yè)使用情況

┌──[root@liruilongs.github.io]-[~] 
└─$grep AnonHugePages /proc/meminfo
AnonHugePages:         0 kB
┌──[root@liruilongs.github.io]-[~] 
└─$

shmem_enabled 用于配置 共享內(nèi)存(如 tmpfs、共享匿名映射)是否啟用透明大頁(yè)(THP)

┌──[root@liruilongs.github.io]-[~] 
└─$cat  /sys/kernel/mm/transparent_hugepage/shmem_enabled 
always within_size advise [never] deny force

這里的 never 表示完全禁用共享內(nèi)存的透明大頁(yè)。常用于數(shù)據(jù)庫(kù)(如 Oracle、MySQL)或高延遲敏感型應(yīng)用,避免因動(dòng)態(tài)內(nèi)存合并引發(fā)性能抖動(dòng)

透明大頁(yè)會(huì)涉及到一個(gè)進(jìn)程 khugepaged,khugepaged 是 Linux 內(nèi)核的一部分,負(fù)責(zé)處理透明大頁(yè)(Transparent HugePages, THP)的管理。透明大頁(yè)是內(nèi)核自動(dòng)將小頁(yè)合并為大頁(yè)以提升性能的機(jī)制,而 khugepaged 就是負(fù)責(zé)這個(gè)合并過(guò)程的守護(hù)進(jìn)程。自動(dòng)掃描內(nèi)存區(qū)域,尋找可以合并的小頁(yè),并嘗試將它們轉(zhuǎn)換為透明大頁(yè)。此過(guò)程在后臺(tái)靜默運(yùn)行,無(wú)需應(yīng)用程序顯式請(qǐng)求。

┌──[root@liruilongs.github.io]-[/sys/devices/system/node] 
└─$ps -eaf | grep khug
root          44       2  0 4月23 ?       00:00:00 [khugepaged]
root       16049    9747  0 10:49 pts/0    00:00:00 grep --color=auto khug

它會(huì)嘗試將多個(gè)常規(guī)小頁(yè)(4KB)合并成 大頁(yè)(2MB 或 1GB),以減少頁(yè)表項(xiàng)數(shù)量,從而提升內(nèi)存訪問(wèn)性能。

控制 khugepaged 的掃描頻率,合并閾值等可以通過(guò)下面的文件修改

┌──[root@liruilongs.github.io]-[/sys/devices/system/node] 
└─$cat /sys/kernel/mm/transparent_hugepage/khugepaged/alloc_sleep_millisecs
60000
┌──[root@liruilongs.github.io]-[/sys/devices/system/node] 
└─$cat  /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs
10000
┌──[root@liruilongs.github.io]-[/sys/devices/system/node] 
└─$

靜態(tài)大頁(yè)

靜態(tài)大頁(yè)需要單獨(dú)配置,使用 sysctl 修改內(nèi)核參數(shù),可以設(shè)置分配的靜態(tài)大頁(yè)的數(shù)量,大頁(yè)內(nèi)存是系統(tǒng)啟動(dòng)時(shí)或通過(guò) sysctl 預(yù)先分配的,這部分內(nèi)存會(huì)被鎖定,普通進(jìn)程無(wú)法使用,所以配置需要考慮清楚

┌──[root@liruilongs.github.io]-[~]
└─$sysctl -a | grep huge
vm.hugetlb_optimize_vmemmap = 0
vm.hugetlb_shm_group = 0
vm.nr_hugepages = 0
vm.nr_hugepages_mempolicy = 0
vm.nr_overcommit_hugepages = 0

vm.nr_hugepages:表示系統(tǒng)要預(yù)留的 大頁(yè)數(shù)量,通過(guò) -w 臨時(shí)配置內(nèi)核參數(shù),配置大頁(yè)數(shù)量為 50

┌──[root@liruilongs.github.io]-[~]
└─$sysctl -w vm.nr_hugepages=50
vm.nr_hugepages = 50

確認(rèn)配置

┌──[root@liruilongs.github.io]-[~]
└─$sysctl -a | grep huge
vm.hugetlb_optimize_vmemmap = 0
vm.hugetlb_shm_group = 0
vm.nr_hugepages = 50
vm.nr_hugepages_mempolicy = 50
vm.nr_overcommit_hugepages = 0

永久生效將配置寫(xiě)入 /etc/sysctl.conf 并執(zhí)行 sysctl -p

可以通過(guò) grub 修改內(nèi)核參數(shù)來(lái)設(shè)置大頁(yè)的數(shù)量以及大小

/etc/default/grub 是 Linux 系統(tǒng)中用于配置 GRUB(GRand Unified Bootloader) 引導(dǎo)程序的核心文件。GRUB 是大多數(shù) Linux 發(fā)行版默認(rèn)的啟動(dòng)管理器,負(fù)責(zé)在系統(tǒng)啟動(dòng)時(shí)加載內(nèi)核和初始化內(nèi)存盤(pán)(initramfs)。該文件定義了 GRUB 的全局行為和啟動(dòng)菜單的默認(rèn)選項(xiàng)。和 上面 grubby 的方式略有區(qū)別

  • hugepages=N : 設(shè)置大頁(yè)的數(shù)量
  • hugepagesz=N 或 default_hugepagesz=N 設(shè)置大頁(yè)大小(默認(rèn) 2MiB)

下面是一個(gè)Demo

┌──[root@liruilongs.github.io]-[~]
└─$cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
┌──[root@liruilongs.github.io]-[~]
└─$

GRUB_CMDLINE_LINUX 傳遞給所有 Linux 內(nèi)核的公共啟動(dòng)參數(shù)(包括默認(rèn)內(nèi)核和恢復(fù)模式內(nèi)核)

┌──[root@liruilongs.github.io]-[~]
└─$vim  /etc/default/grub
 8L, 374B written
┌──[root@liruilongs.github.io]-[~] 
└─$cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="default_hugepagesz=1G hugepages=10 hugepagesz=1G net.ifnames=0 consoleblank=600 console=tty0 console=ttyS0,115200n8 spectre_v2=off nopti noibrs noibpb selinux=0 crashkern
el=512M"
GRUB_DISABLE_RECOVERY="true"

上面的配置 hugepages=10 hugepagesz=1G,靜態(tài)大頁(yè)大小為 1G,數(shù)量為 10

需要說(shuō)明的是,大頁(yè)需要使用連續(xù)的內(nèi)存空間,盡量設(shè)置永久規(guī)則,在開(kāi)機(jī)時(shí)分配大頁(yè),如果系統(tǒng)已經(jīng)運(yùn)行了很久,大量的內(nèi)存碎片,有可能無(wú)法分配大頁(yè),因?yàn)闆](méi)有足夠的連續(xù)內(nèi)存空間。

配置 1G 的靜態(tài)大頁(yè)需要CPU 支持,檢查是否包含 pdpe1gb 標(biāo)簽

┌──[root@liruilongs.github.io]-[~] 
└─$grep pdpe1gb /proc/cpuinfo  
flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology no
nstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_s
ingle ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec 
xgetbv1 arat avx512_vnni md_clear flush_l1d arch_capabilities
.........................................
┌──[root@liruilongs.github.io]-[~] 
└─$

修改之后使用 grub2-mkconfig 生成了新的 GRUB 配置文件。重啟系統(tǒng)使配置生效。

┌──[root@liruilongs.github.io]-[~] 
└─$grub2-mkconfig -o /boot/grub2/grub.cfg
正在生成 grub 配置文件 ...
找到 Linux 鏡像:/boot/vmlinuz-5.10.0-60.139.0.166.oe2203.x86_64
找到 initrd 鏡像:/boot/initramfs-5.10.0-60.139.0.166.oe2203.x86_64.img
找到 Linux 鏡像:/boot/vmlinuz-5.10.0-60.18.0.50.oe2203.x86_64
找到 initrd 鏡像:/boot/initramfs-5.10.0-60.18.0.50.oe2203.x86_64.img
找到 Linux 鏡像:/boot/vmlinuz-0-rescue-f902bd6553f24605a695d4a876a40b7a
找到 initrd 鏡像:/boot/initramfs-0-rescue-f902bd6553f24605a695d4a876a40b7a.img
Adding boot menu entry for UEFI Firmware Settings ...
完成
┌──[root@liruilongs.github.io]-[~] 
└─$reboot

確認(rèn)配置,可以看到 Hugepagesize 是1G,但是 nr_hugepages 大小為 5 ,并不是我們配置的 10,這是什么原因,前面我們講,靜態(tài)大頁(yè)會(huì)直接分配內(nèi)存,即只有配置就會(huì)位于常駐內(nèi)存,當(dāng)系統(tǒng)內(nèi)存沒(méi)有配置的靜態(tài)大頁(yè)大時(shí),系統(tǒng)會(huì)自動(dòng)減少

┌──[root@liruilongs.github.io]-[~] 
└─$cat /proc/meminfo | grep Hug
AnonHugePages:    131072 kB
ShmemHugePages:        0 kB
FileHugePages:         0 kB
HugePages_Total:       5
HugePages_Free:        5
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:    1048576 kB
Hugetlb:         5242880 kB
┌──[root@liruilongs.github.io]-[~] 
└─$sysctl -a | grep huge
vm.hugepage_mig_noalloc = 0
vm.hugepage_nocache_copy = 0
vm.hugepage_pmem_allocall = 0
vm.hugetlb_shm_group = 0
vm.nr_hugepages = 5
vm.nr_hugepages_mempolicy = 5
vm.nr_overcommit_hugepages = 0
┌──[root@liruilongs.github.io]-[~] 
└─$

我們可以使用 free 命令查看內(nèi)存使用情況驗(yàn)證這一點(diǎn)

┌──[root@liruilongs.github.io]-[~] 
└─$free -g
               total        used        free      shared  buff/cache   available
Mem:               7           5           0           0           0           1
Swap:              0           0           0

通過(guò)臨時(shí)修改內(nèi)核參數(shù)調(diào)整靜態(tài)大頁(yè)數(shù)目(實(shí)際調(diào)整需要考慮靜態(tài)大頁(yè)是否使用)

┌──[root@liruilongs.github.io]-[~] 
└─$sysctl -w vm.nr_hugepages=2
vm.nr_hugepages = 2
┌──[root@liruilongs.github.io]-[~] 
└─$sysctl -a | grep nr_hug
vm.nr_hugepages = 2
vm.nr_hugepages_mempolicy = 2
┌──[root@liruilongs.github.io]-[~] 
└─$cat /proc/meminfo | grep Hug
AnonHugePages:    169984 kB
ShmemHugePages:        0 kB
FileHugePages:         0 kB
HugePages_Total:       2
HugePages_Free:        2
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:    1048576 kB
Hugetlb:         2097152 kB

確認(rèn)配置是否生效

┌──[root@liruilongs.github.io]-[~] 
└─$free -g
               total        used        free      shared  buff/cache   available
Mem:               7           2           3           0           0           4
Swap:              0           0           0
┌──[root@liruilongs.github.io]-[~] 
└─$

為了讓進(jìn)程可以使用大頁(yè),進(jìn)程必須進(jìn)行系統(tǒng)函數(shù)調(diào)用,可以調(diào)用 mmap() 函數(shù),或者 shmat() 函數(shù),又或者是 shmget()函數(shù)。如果進(jìn)程使用的是 mmap()系統(tǒng)函數(shù)調(diào)用,則必須掛載-個(gè) hugetlbfs 文件系統(tǒng)。

┌──[root@liruilongs.github.io]-[~]
└─$mkdir /largepage
┌──[root@liruilongs.github.io]-[~]
└─$mount -t hugetlbfs none /largepage

如果在 NUMA 系統(tǒng)上,內(nèi)核將大頁(yè)劃分到所有 NUMA 節(jié)點(diǎn)上,對(duì)應(yīng)的靜態(tài)大頁(yè)參數(shù)需要分別設(shè)置,而不用設(shè)置全局參數(shù)

┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
50
┌──[root@liruilongs.github.io]-[~]
└─$echo 20 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/devices/system/node/node0/meminfo
Node 0 MemTotal:       16082492 kB
Node 0 MemFree:        14969744 kB
Node 0 MemUsed:         1112748 kB
Node 0 SwapCached:            0 kB
Node 0 Active:           562104 kB
Node 0 Inactive:         215520 kB
Node 0 Active(anon):     369388 kB
Node 0 Inactive(anon):        0 kB
Node 0 Active(file):     192716 kB
Node 0 Inactive(file):   215520 kB
Node 0 Unevictable:           0 kB
Node 0 Mlocked:               0 kB
Node 0 Dirty:             28140 kB
Node 0 Writeback:             0 kB
Node 0 FilePages:        420444 kB
Node 0 Mapped:            93924 kB
Node 0 AnonPages:        356560 kB
Node 0 Shmem:             12208 kB
Node 0 KernelStack:        9744 kB
Node 0 PageTables:         6216 kB
Node 0 SecPageTables:         0 kB
Node 0 NFS_Unstable:          0 kB
Node 0 Bounce:                0 kB
Node 0 WritebackTmp:          0 kB
Node 0 KReclaimable:      45436 kB
Node 0 Slab:             111576 kB
Node 0 SReclaimable:      45436 kB
Node 0 SUnreclaim:        66140 kB
Node 0 AnonHugePages:    167936 kB
Node 0 ShmemHugePages:        0 kB
Node 0 ShmemPmdMapped:        0 kB
Node 0 FileHugePages:        0 kB
Node 0 FilePmdMapped:        0 kB
Node 0 Unaccepted:            0 kB
Node 0 HugePages_Total:    20
Node 0 HugePages_Free:     20
Node 0 HugePages_Surp:      0
┌──[root@liruilongs.github.io]-[~]
└─$

透明大頁(yè) vs 靜態(tài)大頁(yè)簡(jiǎn)單比較

特性

透明大頁(yè)(THP)

靜態(tài)大頁(yè)(Huge Pages)

配置方式

內(nèi)核自動(dòng)管理,無(wú)需用戶干預(yù)。

需手動(dòng)預(yù)留(如通過(guò) /etc/default/grub)。

適用場(chǎng)景

通用型應(yīng)用(如 Java、Web 服務(wù))。

高性能計(jì)算、數(shù)據(jù)庫(kù)(如 Oracle、MySQL)。

內(nèi)存碎片化

可能因頻繁合并/拆分導(dǎo)致碎片。

預(yù)留固定內(nèi)存,無(wú)碎片問(wèn)題。

性能穩(wěn)定性

可能因動(dòng)態(tài)合并產(chǎn)生性能波動(dòng)。

性能更穩(wěn)定可控。


責(zé)任編輯:武曉燕 來(lái)源: 山河已無(wú)恙
相關(guān)推薦

2025-08-05 02:45:00

2020-08-10 17:49:25

JVM內(nèi)存溢出

2018-03-16 14:04:45

Linux大內(nèi)存頁(yè)虛擬內(nèi)存管理

2025-09-26 01:00:00

2020-12-15 08:54:06

Linux內(nèi)存碎片化

2025-04-07 04:20:00

Linux操作系統(tǒng)內(nèi)存管理

2010-09-26 10:53:00

JVM內(nèi)存調(diào)優(yōu)設(shè)置

2021-04-30 20:20:36

HugePages大內(nèi)存頁(yè)系統(tǒng)

2023-11-28 08:43:48

2024-08-02 16:25:10

2010-09-25 15:52:27

JVM內(nèi)存JVM

2014-12-19 11:07:40

Java

2019-07-17 15:45:24

Spark內(nèi)存Java

2025-03-25 08:20:00

Linux虛擬內(nèi)存系統(tǒng)

2025-03-25 01:00:00

2018-08-09 16:32:49

內(nèi)存管理框架

2013-10-11 17:32:18

Linux運(yùn)維內(nèi)存管理

2025-10-13 02:11:00

2023-05-29 07:43:32

JVM內(nèi)存調(diào)優(yōu)

2023-02-10 09:28:23

優(yōu)化工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)