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

Linux的mmap文件內存映射機制
字符設備驅動另一種寫法—mmap方法操作LED
Linux用戶空間與內核空間的區別?
labview 中怎么獲取數值的指針(地址)?調用dll時要往里面傳地址
Linux的mmap文件內存映射機制
PCI總線地址空間與系統地址空間的關系
mmap原理詳解

評論