自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文讀懂 HugePages(大內(nèi)存頁)的原理

系統(tǒng) Linux
在介紹 HugePages 之前,我們先來回顧一下 Linux 下 虛擬內(nèi)存 與 物理內(nèi)存 之間的關系。

 [[397159]]

在介紹 HugePages 之前,我們先來回顧一下 Linux 下 虛擬內(nèi)存 與 物理內(nèi)存 之間的關系。

  • 物理內(nèi)存:也就是安裝在計算機中的內(nèi)存條,比如安裝了 2GB 大小的內(nèi)存條,那么物理內(nèi)存地址的范圍就是 0 ~ 2GB。
  • 虛擬內(nèi)存:虛擬的內(nèi)存地址。由于 CPU 只能使用物理內(nèi)存地址,所以需要將虛擬內(nèi)存地址轉(zhuǎn)換為物理內(nèi)存地址才能被 CPU 使用,這個轉(zhuǎn)換過程由 MMU(Memory Management Unit,內(nèi)存管理單元) 來完成。在 32 位的操作系統(tǒng)中,虛擬內(nèi)存空間大小為 0 ~ 4GB。

我們通過 圖1 來描述虛擬內(nèi)存地址轉(zhuǎn)換成物理內(nèi)存地址的過程:

如 圖1 所示,頁表 保存的是虛擬內(nèi)存地址與物理內(nèi)存地址的映射關系,MMU 從 頁表 中找到虛擬內(nèi)存地址所映射的物理內(nèi)存地址,然后把物理內(nèi)存地址提交給 CPU,這個過程與 Hash 算法相似。

內(nèi)存映射是以內(nèi)存頁作為單位的,通常情況下,一個內(nèi)存頁的大小為 4KB(如圖1所示),所以稱為 分頁機制。

一、內(nèi)存映射

我們來看看在 64 位的 Linux 系統(tǒng)中(英特爾 x64 CPU),虛擬內(nèi)存地址轉(zhuǎn)換成物理內(nèi)存地址的過程,如圖2:

從圖2可以看出,Linux 只使用了 64 位虛擬內(nèi)存地址的前 48 位(0 ~ 47位),并且 Linux 把這 48 位虛擬內(nèi)存地址分為 5 個部分,如下:

  • PGD索引:39 ~ 47 位(共9個位),指定在 頁全局目錄(PGD,Page Global Directory)中的索引。
  • PUD索引:30 ~ 38 位(共9個位),指定在 頁上級目錄(PUD,Page Upper Directory)中的索引。
  • PMD索引:21 ~ 29 位(共9個位),指定在 頁中間目錄(PMD,Page Middle Directory)中的索引。
  • PTE索引:12 ~ 20 位(共9個位),指定在 頁表(PT,Page Table)中的索引。
  • 偏移量:0 ~ 11 位(共12個位),指定在物理內(nèi)存頁中的偏移量。

把 圖1 中的 頁表 分為 4 級:頁全局目錄、頁上級目錄、頁中間目錄 和 頁表 目的是為了減少內(nèi)存消耗(思考下為什么可以減少內(nèi)存消耗)。

注意:頁全局目錄、頁上級目錄、頁中間目錄 和 頁表 都占用一個 4KB 大小的物理內(nèi)存頁,由于 64 位內(nèi)存地址占用 8 個字節(jié),所以一個 4KB 大小的物理內(nèi)存頁可以容納 512 個 64 位內(nèi)存地址。

另外,CPU 有個名為 CR3 的寄存器,用于保存 頁全局目錄 的起始物理內(nèi)存地址(如圖2所示)。所以,虛擬內(nèi)存地址轉(zhuǎn)換成物理內(nèi)存地址的過程如下:

  • 從 CR3 寄存器中獲取 頁全局目錄 的物理內(nèi)存地址,然后以虛擬內(nèi)存地址的 39 ~ 47 位作為索引,從 頁全局目錄 中讀取到 頁上級目錄 的物理內(nèi)存地址。
  • 以虛擬內(nèi)存地址的 30 ~ 38 位作為索引,從 頁上級目錄 中讀取到 頁中間目錄 的物理內(nèi)存地址。
  • 以虛擬內(nèi)存地址的 21 ~ 29 位作為索引,從 頁中間目錄 中讀取到 頁表 的物理內(nèi)存地址。
  • 以虛擬內(nèi)存地址的 12 ~ 20 位作為索引,從 頁表 中讀取到 物理內(nèi)存頁 的物理內(nèi)存地址。
  • 以虛擬內(nèi)存地址的 0 ~ 11 位作為 物理內(nèi)存頁 的偏移量,得到最終的物理內(nèi)存地址。

二、HugePages 原理

上面介紹了以 4KB 的內(nèi)存頁作為內(nèi)存映射的單位,但有些場景我們希望使用更大的內(nèi)存頁作為映射單位(如 2MB)。使用更大的內(nèi)存頁作為映射單位有如下好處:

  • 減少 TLB(Translation Lookaside Buffer) 的失效情況。
  • 減少 頁表 的內(nèi)存消耗。
  • 減少 PageFault(缺頁中斷)的次數(shù)。

Tips:TLB 是一塊高速緩存,TLB 緩存虛擬內(nèi)存地址與其映射的物理內(nèi)存地址。MMU 首先從 TLB 查找內(nèi)存映射的關系,如果找到就不用回溯查找頁表。否則,只能根據(jù)虛擬內(nèi)存地址,去頁表中查找其映射的物理內(nèi)存地址。

因為映射的內(nèi)存頁越大,所需要的 頁表 就越小(很容易理解);頁表 越小,TLB 失效的情況就越少。

使用大于 4KB 的內(nèi)存頁作為內(nèi)存映射單位的機制叫 HugePages,目前 Linux 常用的 HugePages 大小為 2MB 和 1GB,我們以 2MB 大小的內(nèi)存頁作為例子。

要映射更大的內(nèi)存頁,只需要增加偏移量部分,如 圖3 所示:

如 圖3 所示,現(xiàn)在把偏移量部分擴展到 21 位(頁表部分被覆蓋了,21 位能夠表示的大小范圍為 0 ~ 2MB),所以 頁中間目錄 直接指向映射的 物理內(nèi)存頁地址。

這樣,就可以減少 頁表 部分的內(nèi)存消耗。由于內(nèi)存映射關系變少,所以 TLB 失效的情況也會減少。

三、HugePages 使用

了解了 HugePages 的原理后,我們來介紹一下怎么使用 HugePages。

HugePages 的使用不像普通內(nèi)存申請那么簡單,而是需要借助 Hugetlb文件系統(tǒng) 來創(chuàng)建,下面將會介紹 HugePages 的使用步驟:

1. 掛載 Hugetlb 文件系統(tǒng)

Hugetlb 文件系統(tǒng)是專門為 HugePages 而創(chuàng)造的,我們可以通過以下命令來掛載一個 Hugetlb 文件系統(tǒng):

  1. $ mkdir /mnt/huge 
  2. $ mount none /mnt/huge -t hugetlbfs 

執(zhí)行完上面的命令后,我們就在 /mnt/huge 目錄下掛載了 Hugetlb 文件系統(tǒng)。

2. 聲明可用 HugePages 數(shù)量

要使用 HugePages,首先要向內(nèi)核聲明可以使用的 HugePages 數(shù)量。/proc/sys/vm/nr_hugepages 文件保存了內(nèi)核可以使用的 HugePages 數(shù)量,我們可以使用以下命令設置新的可用 HugePages 數(shù)量:

  1. $ echo 20 > /proc/sys/vm/nr_hugepages 

上面命令設置了可用的 HugePages 數(shù)量為 20 個(也就是 20 個 2MB 的內(nèi)存頁)。

3. 編寫申請 HugePages 的代碼

要使用 HugePages,必須使用 mmap 系統(tǒng)調(diào)用把虛擬內(nèi)存映射到 Hugetlb 文件系統(tǒng)中的文件,如下代碼:

  1. #include <fcntl.h> 
  2. #include <sys/mman.h> 
  3. #include <errno.h> 
  4. #include <stdio.h> 
  5.  
  6. #define MAP_LENGTH (10*1024*1024) // 10MB 
  7.  
  8. int main() 
  9.     int fd; 
  10.     void * addr; 
  11.  
  12.     // 1. 創(chuàng)建一個 Hugetlb 文件系統(tǒng)的文件 
  13.     fd = open("/mnt/huge/hugepage1", O_CREAT|O_RDWR); 
  14.     if (fd < 0) { 
  15.         perror("open()"); 
  16.         return -1; 
  17.     } 
  18.  
  19.     // 2. 把虛擬內(nèi)存映射到 Hugetlb 文件系統(tǒng)的文件中 
  20.     addr = mmap(0, MAP_LENGTH, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 
  21.     if (addr == MAP_FAILED) { 
  22.         perror("mmap()"); 
  23.         close(fd); 
  24.         unlink("/mnt/huge/hugepage1"); 
  25.         return -1; 
  26.     } 
  27.  
  28.     strcpy(addr, "This is HugePages example..."); 
  29.     printf("%s\n", addr); 
  30.  
  31.     // 3. 使用完成后,解除映射關系 
  32.     munmap(addr, MAP_LENGTH); 
  33.     close(fd); 
  34. 35    unlink("/mnt/huge/hugepage1"); 
  35. 36 
  36. 37    return 0; 
  37. 38 } 

編譯上面的代碼并且執(zhí)行,如果沒有問題,將會輸出以下信息:

  1. This is HugePages example... 

四、總結

本文主要介紹了 HugePages 的原理和使用,雖然 HugePages 有很多優(yōu)點,但也有其不足的地方。比如調(diào)用 fork 系統(tǒng)調(diào)用創(chuàng)建子進程時,內(nèi)核使用了 寫時復制 的技術(可參考《Linux 寫時復制機制原理》一文),在父子進程內(nèi)存發(fā)生改變時,需要復制更大的內(nèi)存頁,從而影響性能。

責任編輯:武曉燕 來源: Linux內(nèi)核那些事
相關推薦

2021-04-30 20:20:36

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

2022-05-12 10:53:42

keepalivevrrp協(xié)議

2025-04-07 03:02:00

電腦內(nèi)存數(shù)據(jù)

2021-12-16 14:45:09

https架構服務端

2024-11-18 11:49:51

2023-01-09 08:14:08

GoHttpServer

2021-10-15 14:28:30

React 組件渲染

2021-10-20 07:18:51

Linux延時隊列

2023-12-22 19:59:15

2021-08-04 16:06:45

DataOps智領云

2021-04-24 09:02:36

Linux 內(nèi)存分配

2020-10-22 09:35:11

線程池核心線程阻塞隊列

2021-02-26 05:24:35

Java垃圾回收

2024-04-10 10:34:34

Cache系統(tǒng)GPU

2018-09-28 14:06:25

前端緩存后端

2025-04-03 10:56:47

2022-09-22 09:00:46

CSS單位

2022-11-06 21:14:02

數(shù)據(jù)驅(qū)動架構數(shù)據(jù)

2021-09-04 19:04:14

配置LogbackJava

2020-05-14 14:52:05

HDFS數(shù)據(jù)集架構
點贊
收藏

51CTO技術棧公眾號