成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

HugePages(大內(nèi)存頁)實現(xiàn)完全解析

系統(tǒng) Linux
免去繁瑣的文件系統(tǒng)掛載過程,我們主要來看看當使用 mmap 系統(tǒng)調(diào)用把虛擬內(nèi)存映射到 hugetlbfs 文件系統(tǒng)的文件時會發(fā)生什么事情。

 [[397163]]

在《一文讀懂 HugePages的原理》一文中介紹了 HugePages(大內(nèi)存頁)的原理和使用,現(xiàn)在我們來分析一下 Linux 內(nèi)核是怎么實現(xiàn) HugePages 分配的。

本文使用 Linux 內(nèi)核 2.6.23 版本

HugePages分配器初始化

在內(nèi)核初始化時,會調(diào)用 hugetlb_init 函數(shù)對 HugePages 分配器進行初始化,其實現(xiàn)如下:

  1. static int __init hugetlb_init(void) 
  2.     unsigned long i; 
  3.  
  4.     // 1. 初始化空閑大內(nèi)存頁鏈表 hugepage_freelists,  
  5.     //    內(nèi)核使用 hugepage_freelists 鏈表把空閑的大內(nèi)存頁連接起來, 
  6.     //    為了分析簡單,我們可以把 MAX_NUMNODES 當成 1 
  7.     for (i = 0; i < MAX_NUMNODES; ++i)           
  8.         INIT_LIST_HEAD(&hugepage_freelists[i]);  
  9.  
  10.     // 2. max_huge_pages 為系統(tǒng)能夠使用的大頁內(nèi)存的數(shù)量, 
  11.     //    由系統(tǒng)啟動項 hugepages 指定, 
  12.     //    這里主要申請大內(nèi)存頁, 并且保存到 hugepage_freelists 鏈表中. 
  13.     for (i = 0; i < max_huge_pages; ++i) { 
  14.         if (!alloc_fresh_huge_page()) 
  15.             break; 
  16.     } 
  17.  
  18.     max_huge_pages = free_huge_pages = nr_huge_pages = i; 
  19.  
  20.     return 0; 

hugetlb_init 函數(shù)主要完成兩個工作:

  • 初始化空閑大內(nèi)存頁鏈表 hugepage_freelists,這個鏈表保存了系統(tǒng)中能夠使用的大內(nèi)存。
  • 為系統(tǒng)申請空閑的大內(nèi)存頁,并且保存到 hugepage_freelists 鏈表中。

我們再來分析下 alloc_fresh_huge_page 函數(shù)是怎么申請大內(nèi)存頁的,其實現(xiàn)如下:

  1.  static int alloc_fresh_huge_page(void) 
  2.  { 
  3.      static int prev_nid; 
  4.      struct page *page; 
  5.      int nid; 
  6.      ... 
  7.      // 1. 申請一個大的物理內(nèi)存頁... 
  8.      page = alloc_pages_node(nid, htlb_alloc_mask|__GFP_COMP|__GFP_NOWARN, 
  9.                             HUGETLB_PAGE_ORDER); 
  10.  
  11.     if (page) { 
  12.         // 2. 設置釋放大內(nèi)存頁的回調(diào)函數(shù)為 free_huge_page 
  13.         set_compound_page_dtor(page, free_huge_page);  
  14.         ... 
  15.         // 3. put_page 函數(shù)將會調(diào)用上面設置的 free_huge_page 函數(shù)把內(nèi)存頁放入到緩存隊列中 
  16.         put_page(page); 
  17.  
  18.         return 1; 
  19.     } 
  20.  
  21.     return 0; 

所以,alloc_fresh_huge_page 函數(shù)主要完成三個工作:

  • 調(diào)用 alloc_pages_node 函數(shù)申請一個大內(nèi)存頁(2MB)。
  • 設置大內(nèi)存頁的釋放回調(diào)函數(shù)為 free_huge_page,當釋放大內(nèi)存頁時,將會調(diào)用這個函數(shù)進行釋放操作。
  • 調(diào)用 put_page 函數(shù)釋放大內(nèi)存頁,其將會調(diào)用 free_huge_page 函數(shù)進行相關操作。

那么,我們來看看 free_huge_page 函數(shù)是怎么釋放大內(nèi)存頁的,其實現(xiàn)如下:

  1. static void free_huge_page(struct page *page) 
  2.     ... 
  3.     enqueue_huge_page(page);     // 把大內(nèi)存頁放置到空閑大內(nèi)存頁鏈表中 
  4.     ... 

free_huge_page 函數(shù)主要調(diào)用 enqueue_huge_page 函數(shù)把大內(nèi)存頁添加到空閑大內(nèi)存頁鏈表中,其實現(xiàn)如下:

  1.  static void enqueue_huge_page(struct page *page) 
  2.  { 
  3.      int nid = page_to_nid(page); // 我們假設這里一定返回 0 
  4.   
  5.      // 把大內(nèi)存頁添加到空閑鏈表 hugepage_freelists 中 
  6.      list_add(&page->lru, &hugepage_freelists[nid]); 
  7.   
  8.      // 增加計數(shù)器 
  9.      free_huge_pages++; 
  10.     free_huge_pages_node[nid]++; 
  11. }                                                              

從上面的實現(xiàn)可知,enqueue_huge_page 函數(shù)只是簡單的把大內(nèi)存頁添加到空閑鏈表 hugepage_freelists 中,并且增加計數(shù)器。

假如我們設置了系統(tǒng)能夠使用的大內(nèi)存頁為 100 個,那么空閑大內(nèi)存頁鏈表 hugepage_freelists 的結(jié)構(gòu)如下圖所示:

所以,HugePages 分配器初始化的調(diào)用鏈為:

  1. hugetlb_init() 
  2.       | 
  3.       +——> alloc_fresh_huge_page() 
  4.                       | 
  5.                       |——> alloc_pages_node() 
  6.                       |——> set_compound_page_dtor() 
  7.                       +——> put_page() 
  8.                                | 
  9.                                +——> free_huge_page() 
  10.                                             | 
  11.                                             +——> enqueue_huge_page() 

hugetlbfs 文件系統(tǒng)

為系統(tǒng)準備好空閑的大內(nèi)存頁后,現(xiàn)在來了解下怎樣分配大內(nèi)存頁。在《一文讀懂 HugePages的原理》一文中介紹過,要申請大內(nèi)存頁,必須使用 mmap 系統(tǒng)調(diào)用把虛擬內(nèi)存映射到 hugetlbfs 文件系統(tǒng)中的文件中。

免去繁瑣的文件系統(tǒng)掛載過程,我們主要來看看當使用 mmap 系統(tǒng)調(diào)用把虛擬內(nèi)存映射到 hugetlbfs 文件系統(tǒng)的文件時會發(fā)生什么事情。

每個文件描述符對象都有個 mmap 的方法,此方法會在調(diào)用 mmap 函數(shù)映射到文件時被觸發(fā),我們來看看 hugetlbfs 文件的 mmap 方法所對應的真實函數(shù),如下:

  1. const struct file_operations hugetlbfs_file_operations = { 
  2.     .mmap               = hugetlbfs_file_mmap, 
  3.     .fsync              = simple_sync_file, 
  4.     .get_unmapped_area  = hugetlb_get_unmapped_area, 
  5. }; 

從上面的代碼可以發(fā)現(xiàn),hugetlbfs 文件的 mmap 方法被設置為 hugetlbfs_file_mmap 函數(shù)。所以當調(diào)用 mmap 函數(shù)映射 hugetlbfs 文件時,將會調(diào)用 hugetlbfs_file_mmap 函數(shù)來處理。

而 hugetlbfs_file_mmap 函數(shù)最主要的工作就是把虛擬內(nèi)存分區(qū)對象的 vm_flags 字段添加 VM_HUGETLB 標志位,如下代碼:

  1. static int  
  2. hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) 
  3.     ... 
  4.     vma->vm_flags |= VM_HUGETLB | VM_RESERVED; // 為虛擬內(nèi)存分區(qū)添加 VM_HUGETLB 標志位 
  5.     ... 
  6.     return ret; 

為虛擬內(nèi)存分區(qū)對象設置 VM_HUGETLB 標志位的作用是:當對虛擬內(nèi)存分區(qū)進行物理內(nèi)存映射時,會進行特殊的處理,下面將會介紹。

虛擬內(nèi)存與物理內(nèi)存映射

使用 mmap 函數(shù)映射到 hugetlbfs 文件后,會返回一個虛擬內(nèi)存地址。當對這個虛擬內(nèi)存地址進行訪問(讀寫)時,由于此虛擬內(nèi)存地址還沒有與物理內(nèi)存地址進行映射,將會觸發(fā) 缺頁異常,內(nèi)核會調(diào)用 do_page_fault 函數(shù)對 缺頁異常 進行修復。

我們來看看整個流程,如下圖所示:

所以,最終會調(diào)用 do_page_fault 函數(shù)對 缺頁異常 進行修復操作,我們來看看 do_page_fault 做了什么工作,實現(xiàn)如下:

  1. asmlinkage void 
  2. __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) 
  3.     ... 
  4.     struct mm_struct *mm; 
  5.     struct vm_area_struct *vma; 
  6.     unsigned long address; 
  7.     ... 
  8.  
  9.     mm = tsk->mm;         // 1. 獲取當前進程對應的內(nèi)存管理對象 
  10.     address = read_cr2(); // 2. 獲取觸發(fā)缺頁異常的虛擬內(nèi)存地址 
  11.  
  12.     ... 
  13.     vma = find_vma(mm, address); // 3. 通過虛擬內(nèi)存地址獲取對應的虛擬內(nèi)存分區(qū)對象 
  14.     ... 
  15.  
  16.     // 4. 調(diào)用 handle_mm_fault 函數(shù)對異常進行修復 
  17.     fault = handle_mm_fault(mm, vma, address, write); 
  18.     ... 
  19.  
  20.     return

上面代碼對 do_page_fault 進行了精簡,精簡后主要完成4個工作:

  • 獲取當前進程對應的內(nèi)存管理對象。
  • 調(diào)用 read_cr2 獲取觸發(fā)缺頁異常的虛擬內(nèi)存地址。
  • 通過觸發(fā) 缺頁異常 的虛擬內(nèi)存地址獲取對應的虛擬內(nèi)存分區(qū)對象。
  • 調(diào)用 handle_mm_fault 函數(shù)對 缺頁異常 進行修復。

我們繼續(xù)來看看 handle_mm_fault 函數(shù)的實現(xiàn),代碼如下:

  1. int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, 
  2.                     unsigned long address, int write_access) 
  3.     ... 
  4.     if (unlikely(is_vm_hugetlb_page(vma))) // 虛擬內(nèi)存分區(qū)是否需要使用 HugePages 
  5.         return hugetlb_fault(mm, vma, address, write_access); // 如果使用 HugePages, 就調(diào)用 hugetlb_fault 進行處理 
  6.     ... 

對 handle_mm_fault 函數(shù)進行精簡后,邏輯就非常清晰。如果虛擬內(nèi)存分區(qū)使用 HugePages,那么就調(diào)用 hugetlb_fault 函數(shù)進行處理(由于我們分析使用 HugePages 的情況,所以剛好進入這個分支)。

hugetlb_fault 函數(shù)主要對進程的頁表進行填充,所以我們先來回顧一下 HugePages 對應的頁表結(jié)構(gòu),如下圖:

從上圖可以看出,使用 HugePages 后,頁中間目錄 直接指向物理內(nèi)存頁。所以,hugetlb_fault 函數(shù)主要就是對 頁中間目錄項 進行填充。實現(xiàn)如下:

  1. int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, 
  2.                   unsigned long address, int write_access) 
  3.     pte_t *ptep; 
  4.     pte_t entry; 
  5.     int ret; 
  6.  
  7.     ptep = huge_pte_alloc(mm, address); // 1. 找到虛擬內(nèi)存地址對應的頁中間目錄項 
  8.     ... 
  9.     entry = *ptep; 
  10.  
  11.     if (pte_none(entry)) { // 如果頁中間目錄項還沒進行映射 
  12.         // 2. 那么調(diào)用 hugetlb_no_page 函數(shù)進行映射操作 
  13.         ret = hugetlb_no_page(mm, vma, address, ptep, write_access); 
  14.         ... 
  15.         return ret; 
  16.     } 
  17.     ... 

對 hugetlb_fault 函數(shù)進行精簡后,主要完成兩個工作:

  • 通過觸發(fā) 缺頁異常 的虛擬內(nèi)存地址找到其對應的 頁中間目錄項。
  • 調(diào)用 hugetlb_no_page 函數(shù)對 頁中間目錄項 進行映射操作。

我們再來看看 hugetlb_no_page 函數(shù)怎么對 頁中間目錄項 進行填充:

  1. static int 
  2. hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, 
  3.                 unsigned long address, pte_t *ptep, int write_access) 
  4.     ... 
  5.     page = find_lock_page(mapping, idx); 
  6.     if (!page) { 
  7.         ... 
  8.         // 1. 從空閑大內(nèi)存頁鏈表 hugepage_freelists 中申請一個大內(nèi)存頁 
  9.         page = alloc_huge_page(vma, address); 
  10.         ... 
  11.     } 
  12.     ... 
  13.     // 2. 通過大內(nèi)存頁的物理地址生成頁中間目錄項的值 
  14.     new_pte = make_huge_pte(vma, page, ((vma->vm_flags & VM_WRITE) 
  15.                                             && (vma->vm_flags & VM_SHARED))); 
  16.  
  17.     // 3. 設置頁中間目錄項的值為上面生成的值 
  18.     set_huge_pte_at(mm, address, ptep, new_pte); 
  19.     ... 
  20.     return ret; 

通過對 hugetlb_no_page 函數(shù)進行精簡后,主要完成3個工作:

  • 調(diào)用 alloc_huge_page 函數(shù)從空閑大內(nèi)存頁鏈表 hugepage_freelists 中申請一個大內(nèi)存頁。
  • 通過大內(nèi)存頁的物理地址生成頁中間目錄項的值。
  • 設置頁中間目錄項的值為上面生成的值。

至此,HugePages 的映射過程已經(jīng)完成。

還有個問題,就是 CPU 怎么知道 頁中間表項 指向的是 頁表 還是 大內(nèi)存頁 呢?

這是因為 頁中間表項 有個 PSE 的標志位,如果將其設置為1,那么就表明其指向 大內(nèi)存頁 ,否則就指向 頁表。

總結(jié)

本文介紹了 HugePages 實現(xiàn)的整個流程,當然本文也只是介紹了申請內(nèi)存的流程,釋放內(nèi)存的流程并沒有分析,如果有興趣的話可以自己查閱源碼。

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

2021-04-30 19:53:53

HugePages大內(nèi)存頁物理

2018-03-16 14:04:45

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

2010-09-25 09:56:46

JVM最大內(nèi)存

2021-06-21 08:58:14

MySQL數(shù)據(jù)庫Pages

2009-10-23 12:37:52

無線接入技術(shù)

2021-09-05 06:00:47

電腦內(nèi)存Windows

2010-04-23 11:18:05

Ubuntu 10.0

2013-06-04 17:23:55

Android開發(fā)移動開發(fā)Fragment

2011-03-17 16:51:51

SQLServer數(shù)據(jù)加速劑

2024-08-02 16:25:10

2014-07-30 11:12:09

block

2021-11-05 16:33:32

手機內(nèi)存技術(shù)

2020-04-07 11:25:24

JDK14內(nèi)存API

2009-10-21 17:32:30

綜合布線注意事項

2015-03-30 11:18:50

內(nèi)存管理Android

2018-04-24 15:11:43

大內(nèi)存SSD戴爾

2025-04-27 04:22:00

2009-10-28 18:43:34

接入網(wǎng)技術(shù)

2009-11-02 17:59:51

2011-09-07 10:24:01

SQL Server鏡像
點贊
收藏

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

主站蜘蛛池模板: 国产免费又色又爽又黄在线观看 | 国产色在线| 在线观看av网站 | 可以免费观看的av | 亚洲综合激情 | 伊人网在线综合 | 日本成人免费观看 | 日韩一级电影免费观看 | 日本在线一区二区三区 | 黄色网毛片 | 一区二区三区欧美 | 中国xxxx性xxxx产国 | 国产99久久精品一区二区永久免费 | 成人综合视频在线观看 | 中文字字幕在线中文乱码范文 | 精品免费国产一区二区三区四区介绍 | 日本午夜精品一区二区三区 | 91精品国产综合久久久动漫日韩 | 高清人人天天夜夜曰狠狠狠狠 | 亚洲二区视频 | 蜜臀网站 | 亚洲第一福利视频 | 日韩免费视频 | 超碰成人在线观看 | 久久一区二区三区四区五区 | 久久精品久久综合 | 在线午夜 | 午夜在线 | 一区二区三区电影在线观看 | 国产精品二区三区 | 色综合久久久 | 中文字幕在线网 | 亚洲黄色高清视频 | 日韩精品一区二区三区中文在线 | 精品国产一区二区三区在线观看 | av网址在线| 亚洲国产一区在线 | 亚洲欧洲精品一区 | 久久综合久色欧美综合狠狠 | 精品久久九九 | 日本成人中文字幕 |