企業(yè)網(wǎng)站備案 名稱(chēng)沈陽(yáng)seo博客
?
Linux的非連續(xù)內(nèi)存分配,介紹vmalloc
機(jī)制,闡述內(nèi)核如何管理vmalloc
地址空間,以及在此空間內(nèi)進(jìn)行內(nèi)存分配與釋放的流程。
?
描述虛擬內(nèi)存區(qū)
- 管理機(jī)制與數(shù)據(jù)結(jié)構(gòu):
vmalloc
地址空間由資源映射分配器管理,struct vm_struct
負(fù)責(zé)存儲(chǔ)基地址、大小等鍵值對(duì)信息 。其字段包括flags
(如VM_ALLOC
、VM_IOREMAP
)、addr
(內(nèi)存塊起始地址 )、size
(內(nèi)存塊大小 )、next
(指向下一個(gè)vm_struct
的指針 ),所有vm_struct
以地址為序,由vmlist_lock
鎖保護(hù)的鏈表鏈接 。 - 內(nèi)存區(qū)域鏈接與查找:內(nèi)存區(qū)域通過(guò)
next
字段鏈接,以地址排序,為防止溢出,區(qū)域間至少隔一個(gè)頁(yè)面 。內(nèi)核分配新內(nèi)存時(shí),get_vm_area()
線性查找vm_struct
鏈表,kmalloc()
分配結(jié)構(gòu)體空間;進(jìn)行I/O重映射時(shí),直接調(diào)用函數(shù)完成請(qǐng)求區(qū)域映射。
分配非連續(xù)區(qū)域
- API 函數(shù):Linux提供
vmalloc()
、vmalloc_dma()
、vmalloc_32()
在連續(xù)虛擬地址空間分配內(nèi)存,僅size
一個(gè)參數(shù)(值為下一頁(yè)邊距向上取整 ),返回新分配區(qū)域線性地址 。 - 分配步驟:第一步,
get_vm_area()
查找vm_struct
線性鏈表,返回描述區(qū)域的新結(jié)構(gòu)體 ;第二步,vmalloc_area_pages()
分配所需PGD記錄,alloc_area_pmd()
分配PMD記錄,alloc_area_pte()
分配PTE記錄,alloc_page()
分配頁(yè)面 。vmalloc()
更新的頁(yè)表不屬于當(dāng)前進(jìn)程,進(jìn)程訪問(wèn)vmalloc
區(qū)域會(huì)發(fā)生缺頁(yè)中斷異常,中斷處理代碼利用主頁(yè)表信息更新當(dāng)前進(jìn)程頁(yè)表 。
?
?
?
本質(zhì)總結(jié)
- vmalloc() 的內(nèi)存分配機(jī)制:
-
- 本質(zhì):vmalloc() 通過(guò)在虛擬地址空間中預(yù)留連續(xù)的虛擬內(nèi)存,并結(jié)合伙伴分配器(alloc_page())分配物理上可能不連續(xù)的頁(yè)面,構(gòu)建虛擬連續(xù)的內(nèi)存區(qū)域。
- 核心流程:
-
-
- 在引用頁(yè)面表中預(yù)留虛擬地址空間。
- 使用伙伴分配器分配物理頁(yè)面(可能不連續(xù))。
- 將分配的物理頁(yè)面映射到預(yù)留的虛擬地址,形成虛擬連續(xù)的內(nèi)存。
-
-
- 意義:vmalloc() 提供了一種靈活分配大塊虛擬連續(xù)內(nèi)存的方式,適用于需要?jiǎng)討B(tài)分配非連續(xù)物理內(nèi)存的場(chǎng)景,體現(xiàn)了 Linux 虛擬內(nèi)存管理的解耦特性。
- 缺頁(yè)中斷處理(vmalloc 區(qū)域):
-
- 本質(zhì):當(dāng)進(jìn)程訪問(wèn) vmalloc 區(qū)域的頁(yè)面時(shí),若發(fā)生缺頁(yè)中斷,系統(tǒng)通過(guò)復(fù)制引用頁(yè)面表中的頁(yè)表項(xiàng)到進(jìn)程的頁(yè)表中,動(dòng)態(tài)建立虛擬地址到物理頁(yè)面的映射,確保訪問(wèn)的正確性。
- 核心流程:
-
-
- 進(jìn)程 B 訪問(wèn) vmalloc 區(qū)域引發(fā)缺頁(yè)中斷(do_page_fault())。
- 系統(tǒng)從引用頁(yè)面表復(fù)制相關(guān)頁(yè)表項(xiàng)到進(jìn)程 B 的頁(yè)表。
- 完成映射后,進(jìn)程 B 可正常訪問(wèn)該頁(yè)面。
-
-
- 意義:通過(guò)延遲映射(按需分配),Linux 高效管理虛擬內(nèi)存,減少初始內(nèi)存分配開(kāi)銷(xiāo),同時(shí)保證多進(jìn)程共享和訪問(wèn) vmalloc 區(qū)域的正確性。
總體本質(zhì)
vmalloc() 和缺頁(yè)中斷處理體現(xiàn)了 Linux 虛擬內(nèi)存管理的核心思想:虛擬與物理分離、按需分配、動(dòng)態(tài)映射。vmalloc() 提供虛擬連續(xù)內(nèi)存的抽象,物理頁(yè)面通過(guò)伙伴分配器靈活分配;缺頁(yè)中斷則通過(guò)動(dòng)態(tài)頁(yè)表映射實(shí)現(xiàn)按需加載,優(yōu)化內(nèi)存使用效率并支持多進(jìn)程協(xié)作訪問(wèn)。
?
7.3 釋放非連續(xù)內(nèi)存
- 釋放函數(shù):
vfree()
負(fù)責(zé)釋放虛擬內(nèi)存區(qū)域,先線性查找vm_struct
鏈表,找到目標(biāo)區(qū)域后調(diào)用vmfree_area_pages()
。 - 釋放操作:
vmfree_area_pages()
與vmalloc_area_pages()
相反,遍歷頁(yè)表,清除該區(qū)域頁(yè)表記錄和相應(yīng)頁(yè)面 。
7.4 2.6內(nèi)核新特性
- 分配頁(yè)面API變化:2.6內(nèi)核中非連續(xù)內(nèi)存分配基本與2.4內(nèi)核保持一致,但分配頁(yè)面內(nèi)部API有細(xì)微差別。2.4內(nèi)核中
vmalloc_area_pages()
遍歷頁(yè)表并調(diào)用alloc_area_pte()
分配PTE及頁(yè)表;2.6內(nèi)核中頁(yè)表由vmalloc()
預(yù)先分配,存儲(chǔ)在數(shù)組中傳給map_vm_area()
,由其向內(nèi)核頁(yè)表插入頁(yè)面 。 get_vm_area()
函數(shù)變化:get_vm_area()
也有細(xì)微改變,調(diào)用時(shí)仍遍歷vmalloc
虛擬地址空間尋找空閑區(qū)域,調(diào)用者可直接調(diào)用__get_vm_area()
并指明范圍,以只遍歷vmalloc
地址空間一部分,還適用于高級(jí)RISC機(jī)器(ARM)裝載模塊 。- 新接口引入:引入新接口
vmap()
,負(fù)責(zé)向vmalloc
地址空間插入頁(yè)面數(shù)組,僅用于聲音子系統(tǒng)內(nèi)核,具有向后兼容性,可減輕特定供應(yīng)商補(bǔ)丁應(yīng)用負(fù)擔(dān) 。
?
🌍 思考:什么是非連續(xù)內(nèi)存分配?非連續(xù)內(nèi)存分配和 伙伴系統(tǒng)有什么關(guān)系?
什么是非連續(xù)內(nèi)存的分配和釋放?
- 非連續(xù)內(nèi)存分配:
-
- 非連續(xù)內(nèi)存分配是指在 虛擬地址空間 中分配一塊 連續(xù)的虛擬地址,但這些虛擬地址映射到的 物理頁(yè)面 可以是 非連續(xù)的。
- 這與伙伴系統(tǒng)(Buddy System)的連續(xù)內(nèi)存分配不同,后者分配的頁(yè)面在物理內(nèi)存中是連續(xù)的(以 2 的冪次方頁(yè)面塊為單位)。
- 非連續(xù)內(nèi)存分配適用于需要大塊虛擬地址空間但不要求物理連續(xù)性的場(chǎng)景,例如內(nèi)核模塊加載、驅(qū)動(dòng)程序的緩沖區(qū)分配或 I/O 映射。
- 釋放非連續(xù)內(nèi)存:
-
- 釋放非連續(xù)內(nèi)存是將之前分配的虛擬地址空間及其關(guān)聯(lián)的物理頁(yè)面歸還給系統(tǒng)。
- 釋放過(guò)程需要清理頁(yè)表(解除虛擬地址到物理頁(yè)面的映射)并將物理頁(yè)面歸還給伙伴系統(tǒng),以便重新分配。
- 為什么需要非連續(xù)內(nèi)存分配?
-
- 物理內(nèi)存可能因碎片化而無(wú)法提供大塊連續(xù)頁(yè)面,但虛擬地址空間是連續(xù)的,vmalloc 通過(guò)頁(yè)表映射實(shí)現(xiàn)虛擬地址連續(xù)性。
- 適合內(nèi)核中需要大塊內(nèi)存但對(duì)物理連續(xù)性要求不高的場(chǎng)景,如內(nèi)核模塊、設(shè)備驅(qū)動(dòng)或文件系統(tǒng)緩存。
?
vmalloc 機(jī)制概述
vmalloc 是 Linux 內(nèi)核中用于分配非連續(xù)內(nèi)存的主要機(jī)制,運(yùn)行在內(nèi)核虛擬地址空間的一個(gè)專(zhuān)用區(qū)域(稱(chēng)為 vmalloc 地址空間)。它通過(guò)頁(yè)表將連續(xù)的虛擬地址映射到可能非連續(xù)的物理頁(yè)面,解決了物理內(nèi)存碎片問(wèn)題。
- vmalloc 地址空間:
-
- 在 32 位系統(tǒng)中,vmalloc 地址空間通常位于內(nèi)核地址空間的高地址區(qū)域(例如 3GB~4GB 之間的某個(gè)范圍)。
- 在 64 位系統(tǒng)中,vmalloc 地址空間更大,位于內(nèi)核地址空間的特定區(qū)域(由架構(gòu)定義,例如 x86_64 的
VMALLOC_START
到VMALLOC_END
)。 - 這一區(qū)域?qū)iT(mén)用于非連續(xù)內(nèi)存分配,獨(dú)立于直接映射區(qū)域(內(nèi)核通過(guò)直接映射訪問(wèn)物理內(nèi)存)和用戶(hù)空間。
- 核心功能:
-
- 提供連續(xù)的虛擬地址,映射到非連續(xù)的物理頁(yè)面。
- 支持動(dòng)態(tài)分配和釋放,適用于內(nèi)核模塊、驅(qū)動(dòng)程序等場(chǎng)景。
- 通過(guò)頁(yè)表管理虛擬到物理的映射,分配時(shí)可能觸發(fā)缺頁(yè)中斷。
描述虛擬內(nèi)存區(qū)
管理機(jī)制與數(shù)據(jù)結(jié)構(gòu)
管理機(jī)制:
- vmalloc 地址空間由 資源映射分配器 管理,負(fù)責(zé)跟蹤分配的虛擬內(nèi)存區(qū)域。
- 每個(gè)分配的區(qū)域由
struct vm_struct
描述,存儲(chǔ)在全局鏈表中。
數(shù)據(jù)結(jié)構(gòu):struct vm_struct
- 核心字段:
-
-
addr
:內(nèi)存塊的起始虛擬地址。size
:內(nèi)存塊的大小(包括保護(hù)頁(yè)面,向上取整到頁(yè)面邊界)。flags
:標(biāo)志位,例如VM_ALLOC
(普通 vmalloc 分配)、VM_IOREMAP
(I/O 重映射)。next
:指向下一個(gè)vm_struct
的指針,構(gòu)成鏈表。
-
-
- 所有
vm_struct
實(shí)例組成一個(gè)全局鏈表,按虛擬地址排序,由vmlist_lock
(自旋鎖)保護(hù),防止并發(fā)訪問(wèn)導(dǎo)致不一致。
- 所有
內(nèi)存區(qū)域鏈接與查找:
-
- 內(nèi)存區(qū)域通過(guò)
next
字段鏈接成單向鏈表,按地址順序排列。 - 為防止溢出,區(qū)域之間至少間隔一個(gè)頁(yè)面(稱(chēng)為 保護(hù)頁(yè)面,guard page),避免分配區(qū)域相互干擾。
- 分配時(shí):
- 內(nèi)存區(qū)域通過(guò)
-
-
get_vm_area()
線性遍歷vm_struct
鏈表,查找適合的空閑虛擬地址范圍。- 使用
kmalloc()
分配vm_struct
結(jié)構(gòu)體的內(nèi)存。
-
-
- I/O 重映射:
-
-
- 對(duì)于 I/O 映射(如設(shè)備驅(qū)動(dòng)的內(nèi)存映射),內(nèi)核直接調(diào)用
ioremap()
或相關(guān)函數(shù),創(chuàng)建vm_struct
并映射到設(shè)備物理地址。
- 對(duì)于 I/O 映射(如設(shè)備驅(qū)動(dòng)的內(nèi)存映射),內(nèi)核直接調(diào)用
-
2. 分配非連續(xù)區(qū)域
API 函數(shù)
- Linux 提供以下 vmalloc 相關(guān) API,用于在 vmalloc 地址空間分配連續(xù)虛擬地址:
-
vmalloc(size)
:分配指定大小的虛擬內(nèi)存,適合通用場(chǎng)景。vmalloc_dma()
:分配適合 DMA(直接內(nèi)存訪問(wèn))的內(nèi)存(可能限制在特定物理地址范圍)。vmalloc_32()
:分配 32 位地址范圍內(nèi)的內(nèi)存(適用于 32 位設(shè)備在 64 位系統(tǒng)中的兼容性)。
- 參數(shù):
-
- 所有 API 只需一個(gè)
size
參數(shù),實(shí)際分配大小會(huì)向上取整到頁(yè)面邊界(例如 4KB)。
- 所有 API 只需一個(gè)
- 返回值:
-
- 返回分配區(qū)域的起始虛擬地址(連續(xù)的),或 NULL(分配失敗)。
分配步驟
- 查找虛擬地址范圍:
-
- 調(diào)用
get_vm_area()
,線性遍歷vm_struct
鏈表,找到一塊未使用的虛擬地址范圍(考慮保護(hù)頁(yè)面)。 - 創(chuàng)建新的
vm_struct
實(shí)例(通過(guò)kmalloc()
分配),記錄分配的地址和大小。
- 調(diào)用
- 分配頁(yè)面并更新頁(yè)表:
-
- 調(diào)用
vmalloc_area_pages()
,為分配的虛擬地址范圍建立頁(yè)表映射:
- 調(diào)用
-
-
alloc_area_pgd()
:分配或更新頁(yè)面全局目錄(PGD,Page Global Directory)。alloc_area_pmd()
:分配頁(yè)面中間目錄(PMD,Page Middle Directory)。alloc_area_pte()
:分配頁(yè)面表項(xiàng)(PTE,Page Table Entry)。alloc_page()
:通過(guò)伙伴系統(tǒng)分配物理頁(yè)面(可能非連續(xù))。
-
-
- 每個(gè)虛擬頁(yè)面映射到一個(gè)物理頁(yè)面,完成虛擬到物理的映射。
- 缺頁(yè)中斷處理:
-
- vmalloc 分配的頁(yè)表不直接綁定到當(dāng)前進(jìn)程的頁(yè)表。
- 當(dāng)進(jìn)程首次訪問(wèn) vmalloc 分配的虛擬地址時(shí),會(huì)觸發(fā) 缺頁(yè)中斷(page fault)。
- 中斷處理程序根據(jù)內(nèi)核的主頁(yè)表(
init_mm
)信息更新當(dāng)前進(jìn)程的頁(yè)表,確保訪問(wèn)有效。
3. 釋放非連續(xù)內(nèi)存
釋放函數(shù)
- vfree():
-
- 負(fù)責(zé)釋放 vmalloc 分配的虛擬內(nèi)存區(qū)域。
- 輸入?yún)?shù)為虛擬地址(
void *addr
),由調(diào)用者提供。
釋放操作
- 查找目標(biāo)區(qū)域:
-
vfree()
線性遍歷vm_struct
鏈表,找到與輸入地址匹配的vm_struct
實(shí)例。
- 清理頁(yè)表和頁(yè)面:
-
- 調(diào)用
vmfree_area_pages()
,與分配過(guò)程相反:
- 調(diào)用
-
-
- 遍歷相關(guān)頁(yè)表(PGD、PMD、PTE),清除虛擬地址到物理頁(yè)面的映射。
- 釋放關(guān)聯(lián)的物理頁(yè)面(通過(guò)
__free_pages()
放回伙伴系統(tǒng))。
-
-
- 刪除對(duì)應(yīng)的
vm_struct
實(shí)例(通過(guò)kfree()
釋放),并更新vm_struct
鏈表。
- 刪除對(duì)應(yīng)的
- 同步頁(yè)表:
-
- 如果當(dāng)前進(jìn)程的頁(yè)表引用了被釋放的 vmalloc 區(qū)域,內(nèi)核會(huì)同步更新頁(yè)表(例如通過(guò) TLB 刷新)。
vmalloc 機(jī)制的核心特點(diǎn)
- 虛擬地址連續(xù),物理地址非連續(xù):
-
- vmalloc 通過(guò)頁(yè)表映射實(shí)現(xiàn)虛擬地址連續(xù)性,物理頁(yè)面可來(lái)自伙伴系統(tǒng)的任意空閑頁(yè)面。
- 開(kāi)銷(xiāo)較高:
-
- 相比伙伴系統(tǒng)的連續(xù)內(nèi)存分配,vmalloc 需要管理頁(yè)表,分配和釋放涉及多次頁(yè)面操作,性能開(kāi)銷(xiāo)較大。
- 適用場(chǎng)景:
-
- 內(nèi)核模塊加載(需要大塊虛擬地址空間)。
- 設(shè)備驅(qū)動(dòng)的緩沖區(qū)分配(尤其是 I/O 映射)。
- 某些文件系統(tǒng)或網(wǎng)絡(luò)協(xié)議棧的臨時(shí)緩沖區(qū)。
- 限制:
-
- vmalloc 區(qū)域的內(nèi)存不能直接用于 DMA(除非使用
vmalloc_dma()
)。 - 分配和釋放的開(kāi)銷(xiāo)較高,不適合高頻分配場(chǎng)景。
- vmalloc 區(qū)域的內(nèi)存不能直接用于 DMA(除非使用
- 非連續(xù)內(nèi)存分配與釋放:
-
- 非連續(xù)內(nèi)存分配通過(guò) vmalloc 在虛擬地址空間分配連續(xù)地址,映射到非連續(xù)物理頁(yè)面,適用于物理內(nèi)存碎片化場(chǎng)景。
- 釋放過(guò)程通過(guò)
vfree()
清理頁(yè)表映射并歸還物理頁(yè)面到伙伴系統(tǒng)。
- vmalloc 機(jī)制:
-
- 管理:通過(guò)
vm_struct
鏈表和vmlist_lock
管理 vmalloc 地址空間。 - 分配:
get_vm_area()
查找虛擬地址,vmalloc_area_pages()
分配頁(yè)面并更新頁(yè)表,觸發(fā)缺頁(yè)中斷動(dòng)態(tài)更新進(jìn)程頁(yè)表。 - 釋放:
vfree()
查找目標(biāo)區(qū)域,vmfree_area_pages()
清理頁(yè)表并釋放頁(yè)面。
- 管理:通過(guò)
- 與伙伴系統(tǒng)的關(guān)系:
-
- vmalloc 依賴(lài)伙伴系統(tǒng)分配物理頁(yè)面(通過(guò)
alloc_page()
)。 - 釋放時(shí),物理頁(yè)面通過(guò)
__free_pages()
歸還給伙伴系統(tǒng)。
- vmalloc 依賴(lài)伙伴系統(tǒng)分配物理頁(yè)面(通過(guò)
Artifact:vmalloc 分配流程偽代碼
/* 分配非連續(xù)內(nèi)存 */
void *vmalloc(size_t size) {// 1. 查找可用虛擬地址范圍struct vm_struct *area = get_vm_area(size);if (!area)return NULL;
//2. 分配物理頁(yè)面并建立頁(yè)表映射
if (vmalloc_area_pages(area) < 0) {kfree(area);return NULL;
}
// 3. 返回虛擬地址
return area->addr;
}
/* 查找虛擬地址范圍 */
struct vm_struct *get_vm_area(size_t size) {struct vm_struct *area = kmalloc(sizeof(struct vm_struct));if (!area)return NULL;
// 線性查找 vm_struct 鏈表,找到合適地址
spin_lock(&vmlist_lock);
area->addr = find_free_vaddr(size); // 確保地址連續(xù)且有保護(hù)頁(yè)面
area->size = roundup(size, PAGE_SIZE);
insert_vmlist(area); // 插入鏈表,按地址排序
spin_unlock(&vmlist_lock);return area;
}
/* 分配頁(yè)面并更新頁(yè)表 */
int vmalloc_area_pages(struct vm_struct *area) {for (vaddr = area->addr; vaddr < area->addr + area->size; vaddr += PAGE_SIZE) {struct page *page = alloc_page(GFP_KERNEL); // 通過(guò)伙伴系統(tǒng)分配頁(yè)面if (!page)return -ENOMEM;map_page_to_vaddr(vaddr, page); // 更新 PGD/PMD/PTE}return 0;
}
/* 釋放非連續(xù)內(nèi)存 */
void vfree(void *addr) {// 1. 查找 vm_structspin_lock(&vmlist_lock);struct vm_struct *area = find_vm_struct(addr);if (!area) {spin_unlock(&vmlist_lock);return;}remove_vmlist(area); // 從鏈表移除spin_unlock(&vmlist_lock);
// 2. 清理頁(yè)表并釋放頁(yè)面
vmfree_area_pages(area);// 3. 釋放 vm_struct
kfree(area);
}
/* 清理頁(yè)表和頁(yè)面 */
void vmfree_area_pages(struct vm_struct *area) {for (vaddr = area->addr; vaddr < area->addr + area->size; vaddr += PAGE_SIZE) {struct page *page = vaddr_to_page(vaddr);unmap_page(vaddr); // 清除頁(yè)表映射__free_pages(page, 0); // 歸還頁(yè)面到伙伴系統(tǒng)}
}
?
🌍 思考:vmalloc 和 kmalloc 有什么區(qū)別?分配的連續(xù)虛擬地址和物理頁(yè)面 是通過(guò)頁(yè)表對(duì)應(yīng)起來(lái)的嗎?
vmalloc 和 kmalloc 的區(qū)別
內(nèi)存分配方式:
- kmalloc:分配物理連續(xù)的內(nèi)存,適用于較小的內(nèi)存塊(通常不超過(guò)幾頁(yè)),直接從內(nèi)核的頁(yè)面分配器獲取連續(xù)的物理頁(yè)面。效率高,適合高性能場(chǎng)景(如設(shè)備驅(qū)動(dòng)程序)。
- vmalloc:分配虛擬連續(xù)但物理上可能不連續(xù)的內(nèi)存,適合需要較大內(nèi)存塊的場(chǎng)景。vmalloc 通過(guò)在虛擬地址空間中預(yù)留連續(xù)區(qū)域,并通過(guò)頁(yè)表映射到可能不連續(xù)的物理頁(yè)面。
內(nèi)存大小限制:
- kmalloc:受限于物理內(nèi)存的連續(xù)性,分配大小通常較小(一般不超過(guò) 4MB,具體取決于系統(tǒng)配置)。
- vmalloc:可以分配較大的內(nèi)存塊(通常用于幾十 KB 到幾 MB 的分配),但分配和訪問(wèn)效率低于 kmalloc,因?yàn)樾枰~外的頁(yè)表操作。
性能與開(kāi)銷(xiāo):
- kmalloc:物理連續(xù)內(nèi)存訪問(wèn)效率高,頁(yè)表映射簡(jiǎn)單,開(kāi)銷(xiāo)小。
- vmalloc:由于物理頁(yè)面可能不連續(xù),需要通過(guò)頁(yè)表進(jìn)行虛擬到物理的映射,訪問(wèn)時(shí)可能引發(fā) TLB(Translation Lookaside Buffer)緩存未命中,導(dǎo)致性能開(kāi)銷(xiāo)較大。
使用場(chǎng)景:
- kmalloc:適用于內(nèi)核中需要高性能、小塊內(nèi)存的場(chǎng)景,如設(shè)備驅(qū)動(dòng)、緩沖區(qū)等。
- vmalloc:適用于需要大塊內(nèi)存但不要求物理連續(xù)的場(chǎng)景,如模塊加載、大型數(shù)據(jù)結(jié)構(gòu)分配等。
連續(xù)虛擬地址與物理頁(yè)面的頁(yè)表對(duì)應(yīng)關(guān)系
vmalloc 分配的連續(xù)虛擬地址與物理頁(yè)面是通過(guò)頁(yè)表對(duì)應(yīng)起來(lái)的。
- 工作原理:
-
- vmalloc 在虛擬地址空間中預(yù)留一塊連續(xù)的虛擬地址范圍。
- 通過(guò)伙伴分配器(alloc_page())獲取物理頁(yè)面,這些頁(yè)面在物理內(nèi)存中可能不連續(xù)。
- 內(nèi)核為這些物理頁(yè)面在頁(yè)表中創(chuàng)建映射,將虛擬地址映射到對(duì)應(yīng)的物理頁(yè)面。
- 頁(yè)表存儲(chǔ)在內(nèi)核的引用頁(yè)面表中,必要時(shí)(例如缺頁(yè)中斷)會(huì)將頁(yè)表項(xiàng)復(fù)制到訪問(wèn)該內(nèi)存的進(jìn)程的頁(yè)表中。
- 頁(yè)表的作用:
-
- 頁(yè)表維護(hù)虛擬地址到物理地址的映射關(guān)系,確保進(jìn)程訪問(wèn) vmalloc 分配的虛擬地址時(shí),能夠正確找到對(duì)應(yīng)的物理頁(yè)面。
- 對(duì)于 vmalloc 區(qū)域,頁(yè)表映射是動(dòng)態(tài)的,可能在缺頁(yè)中斷(do_page_fault())時(shí)按需建立,以減少初始分配開(kāi)銷(xiāo)。
- 與 kmalloc 的區(qū)別:
-
- kmalloc 分配的內(nèi)存物理連續(xù),通常只需要單一的頁(yè)表項(xiàng)或簡(jiǎn)單的線性映射。
- vmalloc 的物理頁(yè)面可能分散,頁(yè)表需要為每個(gè)頁(yè)面單獨(dú)建立映射,增加了復(fù)雜性和開(kāi)銷(xiāo)。
總結(jié)
- vmalloc vs kmalloc:
-
- vmalloc 提供虛擬連續(xù)、物理可能不連續(xù)的大塊內(nèi)存,適合需要較大內(nèi)存的場(chǎng)景,但性能開(kāi)銷(xiāo)較高。
- kmalloc 提供物理連續(xù)的小塊內(nèi)存,性能更高,適合小規(guī)模、高效內(nèi)存分配。
- 頁(yè)表對(duì)應(yīng):
-
- vmalloc 的連續(xù)虛擬地址通過(guò)頁(yè)表映射到可能不連續(xù)的物理頁(yè)面,頁(yè)表動(dòng)態(tài)管理映射關(guān)系。
- kmalloc 的物理連續(xù)性簡(jiǎn)化了頁(yè)表映射,通常是直接的線性映射。
體現(xiàn)Linux 虛擬內(nèi)存管理的靈活性:vmalloc 強(qiáng)調(diào)虛擬地址的連續(xù)性和大塊分配,kmalloc 強(qiáng)調(diào)物理連續(xù)性和高性能,頁(yè)表則是兩者連接虛擬與物理內(nèi)存的關(guān)鍵機(jī)制。
?
🌍 思考:為什么 vmalloc 即使有頁(yè)表的映射也會(huì)發(fā)生缺頁(yè)中斷?頁(yè)表項(xiàng)有映射直接去物理地址訪問(wèn)內(nèi)容不就好了?為什么會(huì)缺頁(yè)呢?
頁(yè)表映射的建立時(shí)機(jī)和實(shí)際物理內(nèi)存分配的時(shí)機(jī):
vmalloc的延遲分配機(jī)制
vmalloc采用的是 延遲分配(lazy allocation)策略:
vmalloc()調(diào)用時(shí):
- 只是在vmalloc區(qū)域分配了虛擬地址空間
- 建立了VMA(Virtual Memory Area)結(jié)構(gòu)
- 并沒(méi)有立即分配物理頁(yè)面。也沒(méi)有建立完整的頁(yè)表映射
首次訪問(wèn)時(shí):
- CPU訪問(wèn)虛擬地址時(shí)發(fā)現(xiàn)頁(yè)表項(xiàng)為空或無(wú)效。
- 觸發(fā)缺頁(yè)中斷(page fault)。
- 內(nèi)核在缺頁(yè)處理函數(shù)中才真正分配物理頁(yè)面。
- 建立虛擬地址到物理地址的頁(yè)表映射。
為什么這樣設(shè)計(jì)?
內(nèi)存效率考慮:
- 很多時(shí)候分配的內(nèi)存并不會(huì)立即全部使用。
- 延遲分配可以避免浪費(fèi)物理內(nèi)存。
- 只有真正訪問(wèn)的頁(yè)面才會(huì)占用物理內(nèi)存。
性能優(yōu)化:
- vmalloc()調(diào)用可以很快返回。
- 避免在分配時(shí)進(jìn)行大量的頁(yè)表操作。
- kmalloc:立即分配物理內(nèi)存和建立映射。
- 用戶(hù)空間malloc:也采用延遲分配,通過(guò)缺頁(yè)中斷按需分配。
- vmalloc:延遲分配,通過(guò)缺頁(yè)中斷按需分配。
vmalloc的缺頁(yè)中斷是其設(shè)計(jì)的核心特性,而不是bug,這種機(jī)制實(shí)現(xiàn)了內(nèi)存的按需分配。
🌍 思考: kmalloc 為什么采樣立即分配物理內(nèi)存?kmalloc 建立的映射是頁(yè)表映射嗎?如果是的話, kmalloc 運(yùn)行在內(nèi)核態(tài)為什么會(huì)有頁(yè)表呢?
kmalloc為什么立即分配物理內(nèi)存?
設(shè)計(jì)目的不同:
- kmalloc主要用于內(nèi)核數(shù)據(jù)結(jié)構(gòu)的分配(如task_struct、文件描述符等)
- 這些數(shù)據(jù)結(jié)構(gòu)通常需要立即使用,延遲分配反而會(huì)增加開(kāi)銷(xiāo)
- 內(nèi)核代碼路徑要求確定性和低延遲,不能容忍缺頁(yè)中斷
使用場(chǎng)景特點(diǎn):
- 分配的內(nèi)存塊通常較小(幾十字節(jié)到幾KB)
- 分配后立即訪問(wèn)的概率很高
- 內(nèi)核中斷處理、系統(tǒng)調(diào)用等關(guān)鍵路徑不能被缺頁(yè)中斷打斷
kmalloc的映射機(jī)制
kmalloc建立的不是頁(yè)表映射,而是線性映射!
線性映射 vs 頁(yè)表映射
線性映射(Linear Mapping):
// 簡(jiǎn)化的線性映射關(guān)系
virtual_addr = physical_addr + PAGE_OFFSET
// 例如:物理地址0x1000 -> 虛擬地址0xC0001000(32位系統(tǒng))
特點(diǎn):
- 虛擬地址和物理地址有固定的偏移關(guān)系
- 不需要頁(yè)表查找,MMU直接通過(guò)偏移計(jì)算
- 訪問(wèn)速度最快,沒(méi)有TLB miss的風(fēng)險(xiǎn)
內(nèi)核態(tài)為什么有頁(yè)表?
內(nèi)核態(tài)確實(shí)有頁(yè)表,但用途不同:
內(nèi)核虛擬地址空間布局
高地址
┌─────────────────┐
│ 固定映射區(qū) │ (fixmap)├─────────────────┤
│ vmalloc區(qū)域 │ ← 需要頁(yè)表映射
├─────────────────┤
│ 線性映射區(qū) │ ← kmalloc在這里,線性映射
├─────────────────┤
│ 內(nèi)核代碼/數(shù)據(jù) │ ← 線性映射
└─────────────────┘
低地址 (PAGE_OFFSET)
為什么內(nèi)核需要頁(yè)表?
- 線性映射區(qū):直接映射,不需要復(fù)雜頁(yè)表
- vmalloc區(qū):動(dòng)態(tài)映射,需要頁(yè)表
- 用戶(hù)空間訪問(wèn):內(nèi)核需要訪問(wèn)用戶(hù)態(tài)內(nèi)存
- 設(shè)備內(nèi)存映射:ioremap等需要頁(yè)表
// 內(nèi)核訪問(wèn)用戶(hù)空間數(shù)據(jù)
copy_from_user(kernel_buf, user_ptr, size);
// 這時(shí)內(nèi)核需要通過(guò)頁(yè)表訪問(wèn)用戶(hù)虛擬地址
特性 | kmalloc | vmalloc |
映射方式 | 線性映射 | 頁(yè)表映射 |
分配時(shí)機(jī) | 立即分配 | 延遲分配 |
物理內(nèi)存 | 連續(xù) | 可以不連續(xù) |
訪問(wèn)速度 | 最快 | 較慢(需要頁(yè)表查找) |
缺頁(yè)中斷 | 不會(huì)發(fā)生 | 首次訪問(wèn)會(huì)發(fā)生 |
使用場(chǎng)景 | 內(nèi)核數(shù)據(jù)結(jié)構(gòu) | 大塊內(nèi)存、驅(qū)動(dòng)緩沖區(qū) |
所以kmalloc和vmalloc是為不同場(chǎng)景設(shè)計(jì)的兩套內(nèi)存分配機(jī)制,各有其合理性。內(nèi)核態(tài)確實(shí)有頁(yè)表,但kmalloc使用的是更高效的線性映射機(jī)制。
?
🌍 思考:內(nèi)核虛擬地址空間 是內(nèi)核進(jìn)程的虛擬地址空間嗎?
內(nèi)核虛擬地址空間 ≠ 內(nèi)核進(jìn)程的虛擬地址空間
內(nèi)核虛擬地址空間:
- 是全局共享的地址空間
- 所有進(jìn)程在內(nèi)核態(tài)時(shí)都使用同一套內(nèi)核虛擬地址空間
- 不屬于任何特定進(jìn)程
進(jìn)程的虛擬地址空間:
- 每個(gè)進(jìn)程有自己獨(dú)立的用戶(hù)態(tài)虛擬地址空間
- 進(jìn)程切換時(shí)用戶(hù)態(tài)地址空間會(huì)切換
- 但內(nèi)核態(tài)地址空間保持不變
完整的虛擬地址空間布局(以32位為例):
4GB ┌─────────────────┐│ ││ 內(nèi)核虛擬地址空間 │ ← 所有進(jìn)程共享│ │ (內(nèi)核代碼、kmalloc、vmalloc等)
3GB ├─────────────────┤│ ││ 用戶(hù)虛擬地址空間 │ ← 每個(gè)進(jìn)程獨(dú)有│ │ (進(jìn)程切換時(shí)會(huì)變化)
0GB └─────────────────┘
為什么這樣設(shè)計(jì)?
內(nèi)核代碼共享:
- 內(nèi)核代碼和數(shù)據(jù)結(jié)構(gòu)需要被所有進(jìn)程共享
- 如果每個(gè)進(jìn)程都有獨(dú)立的內(nèi)核地址空間,會(huì)造成巨大浪費(fèi)
系統(tǒng)調(diào)用一致性:
- 無(wú)論哪個(gè)進(jìn)程調(diào)用系統(tǒng)調(diào)用,內(nèi)核函數(shù)的地址都是一樣的
sys_open()
函數(shù)的虛擬地址對(duì)所有進(jìn)程都相同
實(shí)際例子:
// 進(jìn)程A在內(nèi)核態(tài)
kmalloc(1024); // 返回地址 0xc1234000
// 進(jìn)程B在內(nèi)核態(tài)
kmalloc(1024); // 返回地址 0xc1235000
// 兩個(gè)進(jìn)程看到的內(nèi)核虛擬地址空間是完全相同的!
// 0xc1234000 對(duì)進(jìn)程A和進(jìn)程B都指向同一個(gè)物理地址
內(nèi)核虛擬地址空間應(yīng)該理解為:
- 操作系統(tǒng)全局的內(nèi)核態(tài)地址空間
- 被所有進(jìn)程共享的地址空間
- 包含內(nèi)核代碼、內(nèi)核數(shù)據(jù)結(jié)構(gòu)、內(nèi)核堆等
- 不屬于任何特定進(jìn)程
所以更準(zhǔn)確的說(shuō)法是:內(nèi)核虛擬地址空間是系統(tǒng)級(jí)別的共享地址空間,而不是某個(gè)內(nèi)核進(jìn)程的私有地址空間。
?
🌍 思考:內(nèi)核虛擬地址空間layout從高到底是固定映射區(qū),vmalloc區(qū)域,線性映射區(qū)和內(nèi)核代碼嗎?
是的。
固定映射區(qū) (Fixmap Area):
- 位于最高地址范圍。
- 用于特定的固定虛擬地址映射,通常是為了一些特殊用途(如硬件寄存器訪問(wèn)或臨時(shí)映射)。
- 地址是固定的,映射關(guān)系在內(nèi)核初始化時(shí)確定。
vmalloc 區(qū)域 (vmalloc Area):
- 位于固定映射區(qū)下方。
- 用于動(dòng)態(tài)分配非連續(xù)物理內(nèi)存的虛擬地址空間(通過(guò)
vmalloc()
函數(shù)分配)。 - 適合需要大塊虛擬地址但不要求物理連續(xù)的場(chǎng)景。
線性映射區(qū) (Direct Mapping Area):
- 也稱(chēng)為低端內(nèi)存映射區(qū),位于 vmalloc 區(qū)域下方。
- 這是內(nèi)核虛擬地址空間中最大的部分,用于直接映射物理內(nèi)存(通常是 1:1 映射,虛擬地址與物理地址通過(guò)固定偏移關(guān)聯(lián))。
- 物理內(nèi)存的絕大部分通過(guò)這個(gè)區(qū)域訪問(wèn)。
內(nèi)核代碼區(qū) (Kernel Code/Text Area):
- 位于最低地址范圍。
- 包含內(nèi)核的可執(zhí)行代碼、數(shù)據(jù)段(如全局變量)、只讀數(shù)據(jù)等。
- 通常是固定的、連續(xù)的虛擬地址空間,直接映射到物理內(nèi)存的特定區(qū)域。
補(bǔ)充說(shuō)明:
- 架構(gòu)差異:上述布局以 64 位 x86_64 架構(gòu)為例,不同架構(gòu)(如 ARM64、RISC-V)可能有細(xì)微差異。例如,ARM64 可能有額外的區(qū)域(如模塊映射區(qū))或不同的地址劃分。
- 地址范圍:具體地址范圍取決于內(nèi)核配置(如
CONFIG_VMSPLIT
或CONFIG_PAGE_OFFSET
)和系統(tǒng)架構(gòu)。例如,在 x86_64 上,內(nèi)核虛擬地址通常從0xffff800000000000
開(kāi)始(負(fù)地址空間)。 - 內(nèi)核模塊:內(nèi)核模塊的代碼和數(shù)據(jù)通常分配在 vmalloc 區(qū)域或?qū)iT(mén)的模塊映射區(qū)(視架構(gòu)而定)。
- 安全性:現(xiàn)代內(nèi)核可能啟用 KASLR(Kernel Address Space Layout Randomization),導(dǎo)致內(nèi)核代碼區(qū)和部分映射區(qū)的基地址在啟動(dòng)時(shí)隨機(jī)化。
?
🌍 思考:為什么內(nèi)核虛擬空間也有 vmalloc 區(qū)域?vmlloc 不是用于分配一段連續(xù)的虛擬地址嗎?但是內(nèi)核分配空間為什么會(huì)用到這個(gè)功能?
內(nèi)核虛擬地址空間中設(shè)置 vmalloc 區(qū)域 的主要目的是為了滿(mǎn)足內(nèi)核在某些場(chǎng)景下需要分配虛擬地址連續(xù)但物理地址可以不連續(xù)的內(nèi)存需求。
1. 為什么內(nèi)核需要 vmalloc 區(qū)域?
在內(nèi)核中,內(nèi)存分配通常通過(guò)兩種主要機(jī)制:直接映射區(qū)(線性映射區(qū))和 vmalloc 區(qū)域。直接映射區(qū)通過(guò)固定偏移直接映射到物理內(nèi)存,適合大多數(shù)內(nèi)核內(nèi)存需求(如 kmalloc
分配的內(nèi)存)。然而,直接映射區(qū)要求物理內(nèi)存是連續(xù)的,而在以下場(chǎng)景中,內(nèi)核可能需要分配虛擬地址連續(xù)但物理地址不連續(xù)的內(nèi)存:
- 大塊內(nèi)存分配:當(dāng)需要分配較大的內(nèi)存塊時(shí),物理內(nèi)存可能已經(jīng)碎片化,無(wú)法提供足夠大的連續(xù)物理頁(yè)面。此時(shí),vmalloc 可以通過(guò)映射不連續(xù)的物理頁(yè)面到連續(xù)的虛擬地址空間來(lái)滿(mǎn)足需求。
- 動(dòng)態(tài)內(nèi)存需求:某些內(nèi)核子系統(tǒng)(如文件系統(tǒng)、驅(qū)動(dòng)程序、內(nèi)核模塊)可能需要?jiǎng)討B(tài)分配較大的內(nèi)存塊,但不要求物理地址連續(xù)。vmalloc 提供了這種靈活性。
- 內(nèi)核模塊:加載內(nèi)核模塊時(shí),模塊的代碼和數(shù)據(jù)需要分配在虛擬地址空間中。由于模塊加載是動(dòng)態(tài)的,vmalloc 區(qū)域可以提供連續(xù)的虛擬地址空間來(lái)映射模塊的物理頁(yè)面。
- 特殊用途:一些內(nèi)核功能(如大塊緩沖區(qū)、某些設(shè)備驅(qū)動(dòng)的內(nèi)存映射)可能需要虛擬地址連續(xù)的內(nèi)存,但物理內(nèi)存可以分散,vmalloc 適合這種場(chǎng)景。
2. vmalloc 的作用:分配連續(xù)的虛擬地址
- vmalloc 的核心功能是分配一段虛擬地址連續(xù)的內(nèi)存,而對(duì)應(yīng)的物理頁(yè)面可以是不連續(xù)的。它通過(guò)修改內(nèi)核頁(yè)表,將不連續(xù)的物理頁(yè)面映射到一段連續(xù)的虛擬地址空間。
- 相比之下,
kmalloc
(基于 slab 分配器)分配的內(nèi)存既是虛擬地址連續(xù)的,也是物理地址連續(xù)的,但受限于物理內(nèi)存的碎片化,kmalloc
通常只適合分配較小的內(nèi)存塊(一般不超過(guò)幾頁(yè))。 - vmalloc 的優(yōu)勢(shì)在于它突破了物理內(nèi)存連續(xù)性的限制,適合分配較大或動(dòng)態(tài)的內(nèi)存塊。
3. 內(nèi)核為什么需要這種功能?
內(nèi)核使用 vmalloc 的原因可以歸納為以下幾點(diǎn):
- 內(nèi)存碎片化問(wèn)題:
-
- 在系統(tǒng)長(zhǎng)時(shí)間運(yùn)行后,物理內(nèi)存可能會(huì)變得碎片化,即使有足夠的空閑內(nèi)存,也可能無(wú)法找到足夠大的連續(xù)物理頁(yè)面。
- vmalloc 通過(guò)映射不連續(xù)的物理頁(yè)面到連續(xù)的虛擬地址空間,解決了物理內(nèi)存碎片化的問(wèn)題。
- 動(dòng)態(tài)性和靈活性:
-
- 內(nèi)核模塊、文件系統(tǒng)緩存(如 VFS 的 dentry 或 inode 緩存)、設(shè)備驅(qū)動(dòng)等可能需要?jiǎng)討B(tài)分配內(nèi)存,vmalloc 提供了一種靈活的方式來(lái)滿(mǎn)足這些需求。
- 例如,加載一個(gè)內(nèi)核模塊時(shí),模塊的代碼和數(shù)據(jù)需要分配在虛擬地址空間中,而 vmalloc 區(qū)域可以快速分配連續(xù)的虛擬地址。
- 大塊內(nèi)存需求:
-
- 某些場(chǎng)景(如網(wǎng)絡(luò)堆棧的緩沖區(qū)、某些驅(qū)動(dòng)程序的 DMA 緩沖區(qū))需要較大的內(nèi)存塊,而直接映射區(qū)的連續(xù)物理內(nèi)存可能不足以滿(mǎn)足需求。
- vmalloc 允許內(nèi)核分配大塊虛擬地址空間,而無(wú)需擔(dān)心物理內(nèi)存的連續(xù)性。
- 隔離與管理:
-
- vmalloc 區(qū)域的內(nèi)存分配與直接映射區(qū)的內(nèi)存分配是分開(kāi)的,這有助于內(nèi)核內(nèi)存管理的模塊化。
- 例如,vmalloc 分配的內(nèi)存可以通過(guò)獨(dú)立的頁(yè)表管理,減少對(duì)直接映射區(qū)的干擾。
4. vmalloc 的局限性與權(quán)衡
雖然 vmalloc 提供了靈活性,但它也有一些缺點(diǎn),這也是為什么內(nèi)核不總是使用 vmalloc 的原因:
- 性能開(kāi)銷(xiāo):vmalloc 需要?jiǎng)討B(tài)修改頁(yè)表,每次分配和釋放都會(huì)涉及頁(yè)表操作,相比直接映射區(qū)的
kmalloc
有更高的開(kāi)銷(xiāo)。 - TLB 效率:由于 vmalloc 分配的內(nèi)存可能映射到不連續(xù)的物理頁(yè)面,可能會(huì)導(dǎo)致更多的 TLB(Translation Lookaside Buffer)緩存缺失,影響性能。
- 空間限制:vmalloc 區(qū)域的大小是有限的(在 64 位系統(tǒng)中通常是幾十 GB,具體取決于架構(gòu)和配置),因此不能無(wú)限制使用。
- 不適合 DMA:由于 vmalloc 分配的內(nèi)存物理地址不連續(xù),通常不適合直接用于 DMA(直接內(nèi)存訪問(wèn)),因?yàn)榇蠖鄶?shù)硬件要求 DMA 緩沖區(qū)物理地址連續(xù)。
因此,內(nèi)核在需要物理地址連續(xù)或高性能的場(chǎng)景(如小塊內(nèi)存分配或 DMA)時(shí)會(huì)優(yōu)先使用 kmalloc
,而在需要虛擬地址連續(xù)的大塊內(nèi)存或動(dòng)態(tài)分配時(shí)使用 vmalloc
。
5. 實(shí)際使用場(chǎng)景
以下是一些內(nèi)核中使用 vmalloc 的典型場(chǎng)景:
- 內(nèi)核模塊:加載內(nèi)核模塊時(shí),模塊的代碼、數(shù)據(jù)和符號(hào)表分配在 vmalloc 區(qū)域。
- 文件系統(tǒng):文件系統(tǒng)的元數(shù)據(jù)(如 VFS 的 inode 或 dentry 緩存)可能使用 vmalloc 分配較大的緩沖區(qū)。
- 驅(qū)動(dòng)程序:某些設(shè)備驅(qū)動(dòng)需要分配較大的非連續(xù)內(nèi)存塊(如視頻緩沖區(qū)或網(wǎng)絡(luò)數(shù)據(jù)包緩沖區(qū))。
- 用戶(hù)空間映射:當(dāng)內(nèi)核需要將用戶(hù)空間的內(nèi)存映射到內(nèi)核空間時(shí),可能會(huì)使用 vmalloc 區(qū)域(例如通過(guò)
vmalloc_user
)。
6. 總結(jié)
內(nèi)核虛擬地址空間中的 vmalloc 區(qū)域是為了滿(mǎn)足內(nèi)核在動(dòng)態(tài)分配大塊內(nèi)存或處理物理內(nèi)存碎片化時(shí)的需求。它通過(guò)提供虛擬地址連續(xù)但物理地址可以不連續(xù)的內(nèi)存分配,增強(qiáng)了內(nèi)核的靈活性和內(nèi)存管理能力。盡管 vmalloc 有性能和使用場(chǎng)景的限制,但它在內(nèi)核模塊加載、文件系統(tǒng)、驅(qū)動(dòng)程序等場(chǎng)景中不可或缺。
?
🌍 思考:內(nèi)核中除了有kmalloc,vmalloc還有什么其他malloc嗎?
在 Linux 內(nèi)核中,除了 kmalloc 和 vmalloc,還有其他幾種內(nèi)存分配機(jī)制,用于滿(mǎn)足不同場(chǎng)景下的內(nèi)存需求。這些分配器針對(duì)內(nèi)核的不同用途和約束進(jìn)行了優(yōu)化。以下是內(nèi)核中常見(jiàn)的內(nèi)存分配方式及其特點(diǎn):
1. kmalloc
- 功能:分配物理和虛擬地址都連續(xù)的小塊內(nèi)存,基于 slab 分配器。
- 特點(diǎn):
-
- 適合分配較小的內(nèi)存塊(通常不超過(guò)幾頁(yè))。
- 分配的內(nèi)存位于直接映射區(qū),性能高,適合高頻小塊分配。
- 可通過(guò)
GFP_KERNEL
、GFP_ATOMIC
等標(biāo)志控制分配行為。 - 常用于內(nèi)核數(shù)據(jù)結(jié)構(gòu)(如鏈表、結(jié)構(gòu)體)或需要物理連續(xù)性的場(chǎng)景(如 DMA)。
- 限制:受物理內(nèi)存碎片化影響,難以分配大塊連續(xù)內(nèi)存。
2. vmalloc
- 功能:分配虛擬地址連續(xù)但物理地址可以不連續(xù)的內(nèi)存。
- 特點(diǎn):
-
- 適合分配較大的內(nèi)存塊或動(dòng)態(tài)內(nèi)存(如內(nèi)核模塊、文件系統(tǒng)緩存)。
- 通過(guò)修改頁(yè)表實(shí)現(xiàn),分配的內(nèi)存位于 vmalloc 區(qū)域。
- 性能開(kāi)銷(xiāo)較高(頁(yè)表操作、TLB 失效),不適合 DMA。
- 限制:vmalloc 區(qū)域大小有限,且分配/釋放開(kāi)銷(xiāo)較大。
3. 其他內(nèi)存分配機(jī)制
除了 kmalloc 和 vmalloc,Linux 內(nèi)核還提供以下內(nèi)存分配方式:
a. slab 分配器
- 功能:slab 分配器是 kmalloc 的底層實(shí)現(xiàn),但也可以直接使用(如通過(guò)
kmem_cache_alloc
)。 - 特點(diǎn):
-
- 提供針對(duì)特定對(duì)象的高效緩存分配,減少內(nèi)存碎片。
- 內(nèi)核為常用數(shù)據(jù)結(jié)構(gòu)(如 task_struct、inode)創(chuàng)建專(zhuān)用 slab 緩存。
- 可通過(guò)
kmem_cache_create
創(chuàng)建自定義緩存,kmem_cache_alloc
分配內(nèi)存。
- 用途:適合頻繁分配/釋放固定大小的對(duì)象,如文件系統(tǒng)或網(wǎng)絡(luò)堆棧中的結(jié)構(gòu)體。
- 區(qū)別:kmalloc 是通用的 slab 分配接口,而直接使用 slab 分配器更適合特定對(duì)象的高效管理。
b. get_free_pages (GFP) / __get_free_page
功能:直接分配物理和虛擬地址連續(xù)的整頁(yè)內(nèi)存(基于 buddy 分配器)。
特點(diǎn):
- 通過(guò)
alloc_pages
或__get_free_page
分配一個(gè)或多個(gè)頁(yè)面(通常 4KB 或更大)。 - 提供更底層的控制,適合需要整頁(yè)內(nèi)存的場(chǎng)景。
- 支持
GFP_KERNEL
、GFP_ATOMIC
等標(biāo)志,控制分配行為(如是否允許睡眠)。
用途:常用于需要大塊連續(xù)內(nèi)存的場(chǎng)景,如頁(yè)表分配或某些驅(qū)動(dòng)程序。
區(qū)別:相比 kmalloc,GFP 分配以頁(yè)面為單位,適合較大內(nèi)存需求;kmalloc 更適合小塊內(nèi)存。
?
🌍 思考:內(nèi)核中頁(yè)表是 所有內(nèi)核進(jìn)程共有的嗎?
?
這個(gè)問(wèn)題涉及到一個(gè)重要的概念澄清!
首先:什么是"內(nèi)核進(jìn)程"?
在Linux中,我們需要區(qū)分幾個(gè)概念:
真正的內(nèi)核線程(kernel threads):
- 如
kthreadd
、ksoftirqd
、migration
等 - 這些是純內(nèi)核態(tài)運(yùn)行的線程,沒(méi)有用戶(hù)態(tài)地址空間
普通進(jìn)程在內(nèi)核態(tài)執(zhí)行:
- 用戶(hù)進(jìn)程通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài)
- 這時(shí)不叫"內(nèi)核進(jìn)程",而是"進(jìn)程的內(nèi)核態(tài)執(zhí)行"
頁(yè)表的共享機(jī)制
內(nèi)核態(tài)頁(yè)表:全局共享
// 簡(jiǎn)化的頁(yè)表結(jié)構(gòu)
struct mm_struct {pgd_t *pgd; // 頁(yè)全局目錄};
關(guān)鍵點(diǎn):
- 內(nèi)核態(tài)頁(yè)表項(xiàng)是所有進(jìn)程共享的
- 用戶(hù)態(tài)頁(yè)表項(xiàng)是每個(gè)進(jìn)程獨(dú)有的
- 頁(yè)表的內(nèi)核部分在所有進(jìn)程間保持一致。
?
mm_struct
主要描述用戶(hù)態(tài)虛擬內(nèi)存。內(nèi)核空間的映射信息不在mm_struct中。內(nèi)核空間由全局的數(shù)據(jù)結(jié)構(gòu)管理。
內(nèi)核空間的管理
// 內(nèi)核使用的全局mm_struct
struct mm_struct init_mm = {.mm_rb = RB_ROOT,.pgd = swapper_pg_dir, // 內(nèi)核頁(yè)表.mm_users = ATOMIC_INIT(2),.mm_count = ATOMIC_INIT(1),.mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),.page_table_lock = SPIN_LOCK_UNLOCKED,.mmlist = LIST_HEAD_INIT(init_mm.mmlist),
};
頁(yè)表的實(shí)際共享機(jī)制
進(jìn)程創(chuàng)建時(shí)
static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
{// 1. 分配新的頁(yè)表mm->pgd = pgd_alloc(mm);// 2. 初始化時(shí),內(nèi)核空間映射會(huì)被復(fù)制// 但這些映射指向相同的內(nèi)核物理頁(yè)面// 3. 用戶(hù)空間的VMA會(huì)被復(fù)制或共享copy_vmas(mm, oldmm);
}
?
為什么這樣設(shè)計(jì)?
效率考慮:
- 避免每個(gè)進(jìn)程都維護(hù)完整的內(nèi)核頁(yè)表副本
- 內(nèi)核代碼和數(shù)據(jù)對(duì)所有進(jìn)程都是相同的
一致性保證:
- 所有進(jìn)程在內(nèi)核態(tài)看到的虛擬地址映射都相同
kmalloc()
返回的地址對(duì)所有進(jìn)程都有效
內(nèi)核態(tài)頁(yè)表:
是全局共享的,不屬于特定進(jìn)程。所有進(jìn)程(包括內(nèi)核線程)共享同一套內(nèi)核態(tài)頁(yè)表映射。當(dāng)內(nèi)核態(tài)頁(yè)表更新時(shí),需要同步到所有進(jìn)程
用戶(hù)態(tài)頁(yè)表:每個(gè)進(jìn)程獨(dú)有。進(jìn)程切換時(shí)會(huì)切換用戶(hù)態(tài)頁(yè)表。內(nèi)核態(tài)頁(yè)表部分保持不變。
?
?