Linux的mmap内存映射机制解析
? ? ?VM是面向對象的方法設計的,這里的對象是指內存對象:內存對象是一個軟件抽象的概念,它描述內存區與后備存儲之間的映射.系統可以使用多種類型的后備存儲,比如交換空間,本地或者遠程文件以及幀緩存等等. VM系統對它們統一處理,采用同一操作集操作,比如讀取頁面或者回寫頁面等.每種不同的后備存儲都可以用不同的方法實現這些操作.這樣,系統定義了一套統一的接口,每種后備存儲給出自己的實現方法.這樣,進程的地址空間就被視為一組映射到不同數據對象上的的映射組成.所有的有效地址就是那些映射到數據對象上的地址.這些對象為映射它的頁面提供了持久性的后備存儲.映射使得用戶可以直接尋址這些對象.
? ? 值得提出的是, VM體系結構獨立于Unix系統,所有的Unix系統語義,如正文,數據及堆棧區都可以建構在基本VM系統之上.同時, VM體系結構也是獨立于存儲管理的,存儲管理是由操作系統實施的,如:究竟采取什么樣的對換和請求調頁算法,究竟是采取分段還是分頁機制進行存儲管理,究竟是如何將虛擬地址轉換成為物理地址等等(Linux中是一種叫Three Level Page Table的機制),這些都與內存對象的概念無關.
一、Linux中VM的實現.
? ? ??一個進程應該包括一個mm_struct(memory manage struct),?該結構是進程虛擬地址空間的抽象描述,里面包括了進程虛擬空間的一些管理信息: start_code, end_code, start_data, end_data, start_brk, end_brk等等信息.另外,也有一個指向進程虛存區表(vm_area_struct: virtual memory area)的指針,該鏈是按照虛擬地址的增長順序排列的.在Linux進程的地址空間被分作許多區(vma),每個區(vma)都對應虛擬地址空間上一段連續的區域, vma是可以被共享和保護的獨立實體,這里的vma就是前面提到的內存對象.?
? 下面是vm_area_struct的結構,其中,前半部分是公共的,與類型無關的一些數據成員,如:指向mm_struct的指針,地址范圍等等,后半部分則是與類型相關的成員,其中最重要的是一個指向vm_operation_struct向量表的指針vm_ops, vm_pos向量表是一組虛函數,定義了與vma類型無關的接口.每一個特定的子類,即每種vma類型都必須在向量表中實現這些操作.這里包括了: open, close, unmap, protect, sync, nopage, wppage, swapout這些操作.?
[cpp] view plain copyvm_ops: open, close, no_page, swapin, swapout……
二、驅動中的mmap()函數解析
? ? ? ?設備驅動的mmap實現主要是將一個物理設備的可操作區域(設備空間)映射到一個進程的虛擬地址空間。這樣就可以直接采用指針的方式像訪問內存的方式訪問設備。在驅動中的mmap實現主要是完成一件事,就是實際物理設備的操作區域到進程虛擬空間地址的映射過程。同時也需要保證這段映射的虛擬存儲器區域不會被進程當做一般的空間使用,因此需要添加一系列的保護方式。
[cpp] view plain copy具體的實現分析如下:
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
上面的兩個保護機制就說明了被映射的這段區域具有映射IO的相似性,同時保證這段區域不能隨便的換出。就是建立一個物理頁與虛擬頁之間的關聯性。具體原理是虛擬頁和物理頁之間是以頁表的方式關聯起來,虛擬內存通常大于物理內存,在使用過程中虛擬頁通過頁表關聯一切對應的物理頁,當物理頁不夠時,會選擇性的犧牲一些頁,也就是將物理頁與虛擬頁之間切斷,重現關聯其他的虛擬頁,保證物理內存夠用。在設備驅動中應該具體的虛擬頁和物理頁之間的關系應該是長期的,應該保護起來,不能隨便被別的虛擬頁所替換。具體也可參看關于虛擬存儲器的文章。
接下來就是建立物理頁與虛擬頁之間的關系,即采用函數remap_pfn_range(),具體的參數如下:
int remap_pfn_range(structvm_area_struct *vma, unsigned long addr,unsigned long pfn, unsigned long size, pgprot_t prot)
1、struct vm_area_struct是一個虛擬內存區域結構體,表示虛擬存儲器中的一個內存區域。其中的元素vm_start是指虛擬存儲器中的起始地址。
2、addr也就是虛擬存儲器中的起始地址,通??梢赃x擇addr = vma->vm_start。
3、pfn是指物理存儲器的具體頁號,通常通過物理地址得到對應的物理頁號,具體采用virt_to_phys(dev->data)>>PAGE_SHIFT.首先將虛擬內存轉換到物理內存,然后得到頁號。>>PAGE_SHIFT通常為12,這是因為每一頁的大小剛好是4K,這樣右移12相當于除以4096,得到頁號。
4、size區域大小
5、區域保護機制。
返回值,如果成功返回0,否則正數。
三、系統調用mmap函數解析
? ? ? ? 介紹完VM的基本概念后,我們可以講述mmap和munmap系統調用了.mmap調用實際上就是一個內存對象vma的創建過程,
1、mmap函數
? ? ? ?Linux提供了內存映射函數mmap,它把文件內容映射到一段內存上(準確說是虛擬內存上),通過對這段內存的讀取和修改,實現對文件的讀取和修改?。普通文件被映射到進程地址空間后,進程可以向訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。
先來看一下mmap的函數聲明:
[cpp] view plain copymmap的作用是映射文件描述符fd指定文件的 [off,off + len]區域至調用進程的[addr, addr + len]的內存區域, 如下圖所示:
mmap系統調用的實現過程是
1.先通過文件系統定位要映射的文件;
2.權限檢查,映射的權限不會超過文件打開的方式,也就是說如果文件是以只讀方式打開,那么則不允許建立一個可寫映射;?
3.創建一個vma對象,并對之進行初始化;?
4.調用映射文件的mmap函數,其主要工作是給vm_ops向量表賦值;
5.把該vma鏈入該進程的vma鏈表中,如果可以和前后的vma合并則合并;
6.如果是要求VM_LOCKED(映射區不被換出)方式映射,則發出缺頁請求,把映射頁面讀入內存中.
2、munmap函數
? ? ? munmap(void * start, size_t length):
? ? ? 該調用可以看作是mmap的一個逆過程.它將進程中從start開始length長度的一段區域的映射關閉,如果該區域不是恰好對應一個vma,則有可能會分割幾個或幾個vma.
? ? ? msync(void * start, size_t length, int flags):
? ? ?把映射區域的修改回寫到后備存儲中.因為munmap時并不保證頁面回寫,如果不調用msync,那么有可能在munmap后丟失對映射區的修改.其中flags可以是MS_SYNC, MS_ASYNC, MS_INVALIDATE, MS_SYNC要求回寫完成后才返回, MS_ASYNC發出回寫請求后立即返回, MS_INVALIDATE使用回寫的內容更新該文件的其它映射.該系統調用是通過調用映射文件的sync函數來完成工作的.
? ? ?brk(void * end_data_segement):
將進程的數據段擴展到end_data_segement指定的地址,該系統調用和mmap的實現方式十分相似,同樣是產生一個vma,然后指定其屬性.不過在此之前需要做一些合法性檢查,比如該地址是否大于mm->end_code, end_data_segement和mm->brk之間是否還存在其它vma等等.通過brk產生的vma映射的文件為空,這和匿名映射產生的vma相似,關于匿名映射不做進一步介紹.庫函數malloc就是通過brk實現的.
四、實例解析
? ? ? ?下面這個例子顯示了把文件映射到內存的方法,源代碼是:
[cpp] view plain copy編譯運行此程序:
gcc -Wall mmap.c
./a.out text_filename
上面的方法因為用了PROT_READ,所以只能讀取文件里的內容,不能修改,如果換成PROT_WRITE就可以修改文件的內容了。又由于 用了MAAP_PRIVATE所以只能此進程使用此內存區域,如果換成MAP_SHARED,則可以被其它進程訪問,比如下面的
[cpp] view plain copy五、mmap和共享內存對比
? ? ? 共享內存允許兩個或多個進程共享一給定的存儲區,因為數據不需要來回復制,所以是最快的一種進程間通信機制。共享內存可以通過mmap()映射普通文件(特殊情況下還可以采用匿名映射)機制實現,也可以通過系統V共享內存機制實現。應用接口和原理很簡單,內部機制復雜。為了實現更安全通信,往往還與信號燈等同步機制共同使用。
對比如下:
? ? ? mmap機制:就是在磁盤上建立一個文件,每個進程存儲器里面,單獨開辟一個空間來進行映射。如果多進程的話,那么不會對實際的物理存儲器(主存)消耗太大。
? ? ? shm機制:每個進程的共享內存都直接映射到實際物理存儲器里面。
1、mmap保存到實際硬盤,實際存儲并沒有反映到主存上。優點:儲存量可以很大(多于主存);缺點:進程間讀取和寫入速度要比主存的要慢。
2、shm保存到物理存儲器(主存),實際的儲存量直接反映到主存上。優點,進程間訪問速度(讀寫)比磁盤要快;缺點,儲存量不能非常大(多于主存)
使用上看: 如果分配的存儲量不大,那么使用shm;如果存儲量大,那么使用mmap。總結
以上是生活随笔為你收集整理的Linux的mmap内存映射机制解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在Hadoop 2.3上运行C++程序各
- 下一篇: Linux系统文件I/O编程(一)---