mmap()系統(tǒng)調(diào)用是在用戶進程與內(nèi)核之間共享內(nèi)存區(qū)域的常用方法。我們最近有個程序,需要應(yīng)用進程能夠讀取內(nèi)核驅(qū)動獲取的數(shù)據(jù),經(jīng)過簡單的調(diào)研,決定采用mmap方式。實現(xiàn)起來不難,在驅(qū)動中注冊一個字符設(shè)備,實現(xiàn)該設(shè)備的mmap()方法即可。但這其中有一點小曲折。
在實現(xiàn)設(shè)備的mmap()方法時,需要將物理內(nèi)存映射到應(yīng)用程序通過mmap()系統(tǒng)調(diào)用傳下來的vma中。vma代表的是進程的一段虛擬地址空間。在第一版里,考慮的不全面,利用alloc_pages()將整個內(nèi)存段申請為一段連續(xù)的物理地址空間。然后通過remap_pfn_range()函數(shù)將這段連續(xù)的物理內(nèi)存映射到vma中。經(jīng)過長時間的測試,沒有發(fā)現(xiàn)問題。直到今天,在部署一個老集群時,遇到了問題。這個集群中有很多老機器,內(nèi)存只有十多個G,而且長時間運行后產(chǎn)生了大量的內(nèi)存碎片。從而導致,我們無法獲得足夠的連續(xù)物理內(nèi)存。沒辦法,只好重新調(diào)整驅(qū)動中分配內(nèi)存的方式,改用vmalloc獲取地址空間。
在kernel里,通常有3種申請內(nèi)存的方式:vmalloc, kmalloc, alloc_pages。kmalloc與alloc_pages類似,均是申請連續(xù)的地址空間。而vmalloc則可以申請一段不連續(xù)的物理地址空間,并將其映射到連續(xù)的線性地址上。每次vmalloc之后,內(nèi)核會創(chuàng)建一個vm_struct,用以映射分配到的不連續(xù)的內(nèi)存區(qū)域。vm_struct類似vma,但是又不是一回事。vma是將物理內(nèi)存映射到進程的虛擬地址空間。而vm_struct是將物理內(nèi)存映射到內(nèi)核的線性地址空間。
既然vmalloc拿到的不是連續(xù)的物理內(nèi)存,那么將這些內(nèi)存映射到vma時,就不能直接利用remap_pfn_range()了。
此時可以采用兩種方法,一種是實現(xiàn)vm_operations_struct的fault()方法,用以在缺頁時再映射需要的頁。此方法操作起來較為麻煩。
另一種方法是直接使用remap_vmalloc_range()函數(shù)。該函數(shù)的原型為:
int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,unsigned long pgoff)
其中參數(shù)vma是mmap使用調(diào)用傳下來的,addr即為vmalloc()所分配內(nèi)存的起始地址。而pgoff則為mmap()系統(tǒng)調(diào)用里的偏移參數(shù),可以通過vma->vm_pgoff獲得。該函數(shù)成功執(zhí)行后,返回值為0。如果返回值為負數(shù),則說明出錯了。通常是由于所傳的參數(shù)不正確。
需要注意的是,需要映射到用戶空間的內(nèi)存段,不能直接利用vmalloc()分配,而應(yīng)該使用vmalloc_user()函數(shù)。該函數(shù)除了分配內(nèi)存之外,還會將相應(yīng)的vm_struct結(jié)構(gòu)標記為VM_USERMAP。否則,remap_vmalloc_range將返回錯誤。
在這個項目中碰到的教訓是,永遠不要假設(shè)系統(tǒng)中一定會有超過一個頁的連續(xù)物理內(nèi)存。
不過較新的內(nèi)核具有compact機制,可以整理內(nèi)存碎片。但是,目前至少有一大部分機器不支持,或未開啟此機制。
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1336瀏覽量
40084 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
2903瀏覽量
73541 -
malloc
+關(guān)注
關(guān)注
0文章
52瀏覽量
54
原文標題:vmalloc與mmap
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論