不連續頁分配器補充

vmalloc流程

1. 背景:vmalloc() 要解決的問題

  • kmalloc() 要求 虛擬地址連續,物理頁也連續。大塊內存分配可能失敗。
  • vmalloc() 只保證 虛擬地址連續,物理內存可以由很多不連續的頁拼接。

實現的關鍵就是:

  1. vmalloc 區域 找一塊空閑的虛擬地址。
  2. 分配若干物理頁(可能不連續)。
  3. 建立虛擬地址 → 物理頁的映射。

這三個步驟里,數據結構的角色就是:

  • **vmap_area**:負責管理 vmalloc 區域里的虛擬地址范圍。
  • **vm_struct**:描述一個具體的 vmalloc 內存塊(和用戶返回的 addr 對應)。

2. 關鍵數據結構解析

struct vmap_area

表示 vmalloc 區域中的一個虛擬地址段

struct vmap_area {unsigned long va_start;unsigned long va_end;unsigned long flags;struct rb_node rb_node;         /* address sorted rbtree */struct list_head list;          /* address sorted list */struct llist_node purge_list;    /* "lazy purge" list */struct vm_struct *vm;struct rcu_head rcu_head;
};
  • 內核全局維護一棵紅黑樹和鏈表來管理所有的 vmap_area,保證虛擬地址分配不沖突。
  • 每次 vmalloc() 會新建一個 vmap_area,掛到這棵樹里。

struct vm_struct

表示 一個具體的 vmalloc 塊,用戶代碼拿到的就是 vm_struct->addr

struct vm_struct {struct vm_struct	*next;void			*addr;unsigned long		size;unsigned long		flags;struct page		**pages;unsigned int		nr_pages;phys_addr_t		phys_addr;const void		*caller;
};
  • **pages[]**** 是核心**:記錄了 vmalloc 這片區域實際映射到哪些物理頁。
  • addrvmap_area->va_start,兩者一一對應。
  • vm_struct 通過 vmap_area->vm 與虛擬地址區間關聯。

3. vmalloc() 的流程

vmalloc(size) 為例,流程大致是:

(1) 計算所需頁數

nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;

(2) 在 vmalloc 區域找虛擬地址

  • 調用 alloc_vmap_area()
    • 通過 紅黑樹,在vmalloc區域中查找一塊足夠大的空閑虛擬地址區間;
    • 建立一個新的 struct vmap_area,填好 va_start/va_end
    • 掛到全局紅黑樹/鏈表里。

這一步解決:虛擬地址空間的分配。


(3) 分配物理頁

  • 調用 alloc_page()(實際走伙伴系統),分配 nr_pages 個物理頁。
  • 這些頁可能離散。
  • 把它們存進 vm_struct->pages[]

這一步解決:物理內存的獲取。


(4) 建立映射

  • 調用 map_vm_area() 或更底層的 vmap_page_range()
    • 遍歷 pages[]
    • 在頁表里把 va_start ~ va_end 的虛擬頁,依次映射到對應的物理頁。

這樣,就實現了 虛擬地址連續 → 物理頁不連續 的映射。

如何找到內核線程的頁表?后面解釋


(5) 返回給用戶

  • vm_struct->addr = (void *)vmap_area->va_start
  • 返回給調用者。

調用者得到的是一段看起來“連續”的內存。


4. vmalloc() 與 vmap() 的關系

  • vmalloc() = 自動分配物理頁 + 申請虛擬地址 + 調用 vmap 建立映射
  • vmap(pages[], nr_pages, ...) = 自己提供物理頁數組,直接建立虛擬映射。

所以:

  • **vmalloc()**** 面向使用者**(只要給我一段內存);
  • **vmap()**** 面向更底層**(我已有頁,幫我拼接)。

5. 小結

vmalloc() 的機制可以歸納為三步:

  1. 地址管理
    vmap_area 負責在 vmalloc 區域找一段空閑虛擬地址,并放到全局紅黑樹。
  2. 塊描述
    vm_struct 保存這段虛擬內存的元數據(起始地址、大小、物理頁數組)。
  3. 頁表映射
    把虛擬地址區間映射到 vm_struct->pages[] 里記錄的實際物理頁。

vfree釋放過程

vfree() 被調用時:

  1. 根據 addr 找到對應的 vmap_area
  2. 從紅黑樹和鏈表刪除。
  3. 把物理頁釋放回伙伴系統。
  4. 延遲釋放 vmap_area(放到 purge_list,用 RCU 機制安全回收)。

linux中常用內存分配函數

用戶態 vs 內核態

  • 用戶態 API
    malloc(), brk(), mmap()
    這是 C 庫(glibc)或系統調用提供的接口,進程使用。
    本質上是通過 VMA 管理 + 缺頁時分配物理頁
  • 內核態 API
    alloc_pages(), kmalloc(), vmalloc()
    這是 Linux 內核給自己用的內存分配器接口,驅動/內核子系統用。
    本質上是 直接操作伙伴系統/SLAB/vmalloc 子系統

各方法機制對比

接口使用場景內核實現方式地址連續性使用者
malloc()用戶程序最常用的內存申請glibc 封裝,底層調用 brk()
mmap()
擴展堆/映射匿名頁
用戶虛擬地址連續(物理不一定連續)用戶空間
brk()擴展/收縮 heap(sbrk
系統調用)
修改進程的堆 VMA 邊界,缺頁時由 alloc_pages()
分配物理頁
用戶虛擬地址連續(物理不一定連續)用戶空間
mmap()大塊內存/文件映射/共享內存創建新的 VMA,缺頁時用 alloc_pages()
或從文件讀取到物理頁
用戶虛擬地址連續(物理不一定連續)用戶空間
alloc_pages()分配頁粒度內存伙伴系統分配 struct page物理連續,內核虛擬地址也連續(線性映射區)內核
kmalloc()內核小塊內存(字節/KB 級)SLAB/SLUB 分配器,底層基于 alloc_pages()物理連續 + 內核虛擬連續內核
vmalloc()內核大塊內存(MB 級)從 vmalloc 區找虛擬地址區間,分配不連續物理頁(底層基于alloc_pages()),建立頁表映射虛擬地址連續,物理地址不連續內核

關系梳理

  1. 用戶空間
    • malloc() → 封裝,可能走 brk()mmap()
    • brk()/mmap() → 修改 mm_struct 和 VMA;
    • 缺頁時 → 最終用 alloc_pages() 分配物理頁。
  2. 內核空間
    • alloc_pages() → 最底層接口,直接伙伴系統;
    • kmalloc() → 面向小對象,使用slab分配器,底層用 alloc_pages()
    • vmalloc() → 面向大塊虛擬地址空間,物理頁不連續。底層用 alloc_pages()

總結

  • 用戶態用 malloc()(底層 brk/mmap),本質是修改虛擬內存布局,缺頁時通過 **伙伴系統 ****alloc_pages()** 分配物理頁;
  • 內核態直接用 alloc_pages()kmalloc()(小塊)、vmalloc()(大塊,物理不連續)。

如何找到內核線程的頁表?

“內核線程沒有用戶空間”就會懷疑:那頁表怎么辦?是不是有個“內核專用頁表”?
其實 Linux 內核線程并不是共享一個“內核頁表”,而是借用普通進程的頁表


1. 頁表的基本事實

  • x86/ARM 等架構上,CPU 訪問內存都要走頁表轉換。頁表的基地址存放在控制寄存器(x86 的 CR3,ARM64 的 TTBR0/TTBR1)。
  • Linux 設計:所有進程的頁表都包含了同一份內核態映射(高地址部分的 linear mapping、vmalloc 等)
    • 換句話說,每個進程的 mm_struct->pgd 不同,但其中“內核地址區”是一致的。
    • 所以,只要有一份用戶進程的頁表,就能保證內核地址區始終可用。

2. 普通進程 vs 內核線程

普通用戶進程

  • 每個進程有自己的 mm_struct,里面有獨立的 pgd(頁全局目錄)。
  • 切換進程時,調度器會把 mm->pgd 加載到 CR3
  • 這樣用戶態地址空間不同,但內核態地址映射相同。

內核線程

  • task_struct->mm = NULL,說明它沒有獨立的 mm_structpgd
  • 調度器在切換到內核線程時:
    • 如果發現 mm == NULL,會把 prev->active_mm 借給內核線程,保存到 next->active_mm
    • 并且在切換時 不會切換 CR3,繼續使用原進程的頁表。
  • 內核線程只在內核態執行,不會訪問用戶空間地址,所以根本不在意用戶空間頁表部分。

3. 也就是說:

  • 每個內核線程并沒有單獨的頁表
  • 它們 借用上一個普通進程的頁表,只是用其中的內核映射部分。
  • 這就是 task_struct->active_mm 的意義。

4. “內核頁表”的保存與使用

  • 并不存在一個獨立的“全局內核頁表”。
  • 取而代之:每個進程的頁表都自帶了內核映射部分
  • 內核線程調度時,就繼續使用借來的頁表的內核部分。

總結

內核線程沒有獨立的頁表,它們不會切換到某個“內核專用頁表”。調度到內核線程時,Linux 內核會讓它們 借用上一個進程的頁表(通過 active_mm),只使用其中的內核地址映射部分。由于所有進程的內核區頁表一致,內核線程就能安全運行。

測試驗證

代碼實現

實現一個最小可運行的 內核模塊 示例,專門用來測試 vmalloc() 申請和釋放內存。代碼如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>   // vmalloc/vfree
#include <linux/kernel.h>#define VMALLOC_SIZE (1024 * 1024)  // 申請 1MBstatic void *vmalloc_area = NULL;static int __init vmalloc_test_init(void)
{pr_info("vmalloc_test: module loaded\n");// 使用 vmalloc 申請一塊連續虛擬地址的內存vmalloc_area = vmalloc(VMALLOC_SIZE);if (!vmalloc_area) {pr_err("vmalloc_test: vmalloc failed!\n");return -ENOMEM;}pr_info("vmalloc_test: allocated %d bytes at %pK\n",VMALLOC_SIZE, vmalloc_area);// 寫入測試數據memset(vmalloc_area, 0xAA, VMALLOC_SIZE);pr_info("vmalloc_test: memory initialized with 0xAA\n");return 0;
}static void __exit vmalloc_test_exit(void)
{if (vmalloc_area) {vfree(vmalloc_area);pr_info("vmalloc_test: freed memory at %pK\n", vmalloc_area);}pr_info("vmalloc_test: module unloaded\n");
}module_init(vmalloc_test_init);
module_exit(vmalloc_test_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("congchp");
MODULE_DESCRIPTION("Simple vmalloc test module");
obj-m += vmalloc_test.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

測試結果

dmesg結果:

/proc/vmallocinfo結果:

參考資料

  1. Professional Linux Kernel Architecture,Wolfgang Mauerer
  2. Linux內核深度解析,余華兵
  3. Linux設備驅動開發詳解,宋寶華
  4. linux kernel 4.12

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921615.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921615.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921615.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

bug | 事務粒度不能太大,含demo

刷到一個說法&#xff0c;建議不要使用transaction注解。這個說法不太準確&#xff0c;注解可以用&#xff0c;但標注的事務粒度不能太大&#xff0c;這樣可能會引起數據庫阻塞問題。以下介紹注解事務和編程式事務的兩種用法。 關鍵字&#xff1a;聲明式事務&#xff0c;編程式…

別再看人形機器人了!真正干活的機器人還有這些!

每次提起“機器人”&#xff0c;你腦海中是不是立刻浮現出雙足行走、擬人微笑、還能陪你聊天的那種“人形機器人”&#xff1f;但真相是&#xff1a;人形機器人并非更實用&#xff0c;只是滿足了我們對“人類替代品”的幻想。事實上&#xff0c;機器人的世界遠比我們想象的更豐…

垃圾回收,幾種GC算法及GC機制

1.什么是垃圾回收&#xff1f;如何觸發垃圾回收&#xff1f; 垃圾回收(GC)是自動管理內存的一種機制&#xff0c;它負責自動釋放不再被程序引用的對象所占用的內存&#xff0c;這種機制減少內存泄漏和內存管理錯誤的可能性。可以通過多種方式觸發&#xff1a;內存不足時&#x…

更智能的零售終端設備管理:合規、安全與高效

目錄 引言&#xff1a;為什么零售連鎖和自助終端需要更智能的設備管理&#xff1f; 典型應用場景 1. 便利店連鎖 2. 大型超市 3. 加油站 4. 自助終端 核心功能&#xff0c;驅動高效與安全 1. 批量配置 2. 定時策略同步 3. 設備狀態監控 4. Kiosk 模式&#xff0c;保…

Elasticsearch:向量搜索過濾 - 保持相關性

作者&#xff1a;來自 Elastic Carlos Delgado 僅執行向量搜索以找到與查詢最相似的結果是不夠的。通常需要過濾來縮小搜索結果。本文解釋了在 Elasticsearch 和 Apache Lucene 中向量搜索的過濾是如何工作的。 Elasticsearch 擁有豐富的新功能&#xff0c;幫助你為自己的用例構…

Linux 性能調優之 OOM Killer 的認知與觀測

寫在前面 博文內容涉及到OOM Killer機制,以及利用 Cgroup/dmesg/BPF 觀測 OOM Killer 事件,包括云原生環境下的 OOM Killer 機制的簡單介紹 這是內存調優的最后一篇,之后會分享一些網絡調優相關內容 理解不足小伙伴幫忙指正 ??,生活加油 我不再將這個世界與我所期待的,塑…

webrtc之高通濾波——HighPassFilter源碼及原理分析

文章目錄前言一、導讀二、高通濾波過程1.HighPassFilter的創建1&#xff09;HighPassFilter的作用2&#xff09;開啟條件3&#xff09;開啟配置2.高通濾波整體過程1&#xff09;觸發時機2&#xff09;濾波器創建3&#xff09;高通濾波過程三、算法實現1.原理1&#xff09;濾波器…

《sklearn機器學習——聚類性能指數》同質性,完整性和 V-measure

函數&#xff1a;homogeneity_score 參數&#xff1a; labels_true: array-like, shape [n_samples] 樣本的真實標簽。 labels_pred: array-like, shape [n_samples] 樣本的預測標簽。返回值&#xff1a; h: float 同質性得分&#xff0c;在0到1之間&#xff0c;值越大表示聚…

HarmonyOS 應用開發新范式:深入剖析 Stage 模型與 ArkTS 狀態管理

好的&#xff0c;請看這篇關于 HarmonyOS 應用開發中 Stage 模型與 ArkTS 狀態管理的技術文章。 HarmonyOS 應用開發新范式&#xff1a;深入剖析 Stage 模型與 ArkTS 狀態管理 引言 隨著 HarmonyOS 4、5 的發布以及 API 12 的迭代&#xff0c;HarmonyOS 的應用開發范式已經全面…

一個Java的main方法在JVM中的執行流程

一個Java的main方法在JVM中的執行流程可以分為??四大階段??&#xff1a;??加載 -> 鏈接 -> 初始化 -> 執行??。// HelloWorld.java public class HelloWorld {public static void main(String[] args) {String message "Hello, JVM!";System.out.p…

聚焦診斷管理(DM)的傳輸層設計、診斷服務器實現、事件與通信管理、生命周期與報告五大核心模塊

聚焦診斷管理(DM)的傳輸層設計、診斷服務器實現、事件與通信管理、生命周期與報告五大核心模塊,明確 UDS(ISO 14229-1)與 SOVD(ASAM 服務化診斷)的功能邏輯、交互流程及規范性要求(SWS_DM 系列)。 1 UDS 傳輸層(UDS Transport Layer) 作為 DM 與診斷客戶端的 UDS …

關于npm的鉤子函數

一、npm scripts 的生命周期鉤子&#xff08;Lifecycle Scripts&#xff09; npm 提供了一些 ??特殊的 script 名稱??&#xff0c;它們是 ??生命周期鉤子??&#xff0c;會在特定時機 ??自動執行??。這些鉤子包括&#xff1a; 1.prepublishOnly(在 npm publish之前執…

167.在Vue3中使用OpenLayers模仿共享單車,判斷點是否放在規劃的電子圍欄內

一、前言大家好&#xff0c;這里分享一個 Vue3 OpenLayers 的小案例&#xff1a; 模仿共享單車的電子圍欄功能&#xff0c;用戶在地圖上繪制停泊點時&#xff0c;系統會自動判斷該點是否在規劃好的電子圍欄內&#xff08;多邊形或圓形&#xff09;。這個功能在實際項目中有很大…

鍵盤上面有F3,四,R,F,V,按下沒有反應,維修記錄

打開游戲&#xff0c;按了好幾遍F&#xff0c;結果都沒反應&#xff0c;但是左右上下行走是沒問題的。一臉懵逼&#xff1f;&#xff1f;&#xff1f;打開鍵盤測試網頁&#xff0c;發現有一列沒反應&#xff0c;F1不是&#xff0c;F1我定義了一個快捷鍵&#xff0c;跟測試沖突了…

8051單片機-成為點燈大師

第三章 成為點燈大師 1. 硬件設計 上一章說到&#xff0c;怎么點亮LED燈&#xff0c;很簡單啊&#xff0c;就是把P2口設置成低電平就行了。接下來讓我們更進一步&#xff0c;完成LED閃爍、流水燈實驗2. 軟件設計 2.1 LED閃爍實驗 為了使LED閃爍&#xff0c;我們自然而然的想到要…

Rust 日志庫完全指南:從入門到精通

GitHub 倉庫: https://github.com/zhouByte-hub/rust-study ? 如果這個項目對您有幫助&#xff0c;請給我一個 star&#xff01; 在 Rust 生態系統中&#xff0c;日志處理是一個至關重要的環節。無論是開發小型應用還是大型系統&#xff0c;良好的日志記錄都能幫助我們追蹤問題…

【科研繪圖系列】R語言繪制論文合集圖

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹 數據準備與過濾 統計分析 可視化繪圖 抗藥性分析 系統發育分析 加載R包 數據下載 Supp figure 1 Fig 1a Fig 1c Fig 1d Fig 1e Fig 1f Supp figure 3 Supp figure 4 Supp figure 5…

【c++】從三個類的設計看軟件架構的哲學思考

從三個類的設計看軟件架構的哲學思考 文章目錄從三個類的設計看軟件架構的哲學思考前言一、OP類&#xff1a;系統工程的安全守護者設計特點設計哲學適用場景現實類比二、VarReviser類&#xff1a;版本控制的嚴謹管理者設計特點設計哲學適用場景現實類比三、Model類&#xff1a;…

人工智能優化SEO關鍵詞的實戰策略

本文聚焦智能技術如何革新關鍵詞優化實踐&#xff0c;系統解析提升網站排名的核心路徑。重點探討語義分析如何精準匹配用戶意圖、長尾詞智能挖掘怎樣解鎖高潛力流量&#xff0c;并詳解工具篩選高轉化關鍵詞的五大實用策略。通過實戰案例說明技術如何突破流量增長瓶頸&#xff0…

【c++】c++第一課:命名空間

文章目錄1.C的第?個程序2.命名空間2.1 namespace的價值2.2 namespace的定義2.3 命名空間使?最新的c標準&#xff08;建議收藏&#xff09; 1.C的第?個程序 C兼容C語?絕?多數的語法&#xff0c;所以C語?實現的helloworld依舊可以運?&#xff0c;C中需要把定義?件代碼后…