[转载]linux内存映射mmap原理分析
轉自:http://blog.csdn.net/yusiguyuan/article/details/23388771
?
內存映射,簡而言之就是將用戶空間的一段內存區域映射到內核空間,映射成功后,用戶對這段內存區域的修改可以直接反映到內核空間,同樣,內核空間對這段區域的修改也直接反映用戶空間。那么對于內核空間<---->用戶空間兩者之間需要大量數據傳輸等操作的話效率是非常高的。
以下是一個把普遍文件映射到用戶空間的內存區域的示意圖。 圖一: 二、基本函數 ? ??mmap函數是unix/linux下的系統調用,詳細內容可參考《Unix Netword programming》卷二12.2節。 mmap系統調用并不是完全為了用于共享內存而設計的。它本身提供了不同于一般對普通文件的訪問方式,進程可以像讀寫內存一樣對普通文件的操作。而Posix或系統V的共享內存IPC則純粹用于共享目的,當然mmap()實現共享內存也是其主要應用之一。 ? ? ? ? ??mmap系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間后,進程可以像訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。mmap并不分配空間, 只是將文件映射到調用進程的地址空間里(但是會占掉你的 virutal memory), 然后你就可以用memcpy等操作寫文件, 而不用write()了.寫完后,內存中的內容并不會立即更新到文件中,而是有一段時間的延遲,你可以調用msync()來顯式同步一下, 這樣你所寫的內容就能立即保存到文件里了.這點應該和驅動相關。 不過通過mmap來寫文件這種方式沒辦法增加文件的長度, 因為要映射的長度在調用mmap()的時候就決定了.如果想取消內存映射,可以調用munmap()來取消內存映射 void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)mmap用于把文件映射到內存空間中,簡單說mmap就是把一個文件的內容在內存里面做一個映像。映射成功后,用戶對這段內存區域的修改可以直接反映到內核空間,同樣,內核空間對這段區域的修改也直接反映用戶空間。那么對于內核空間<---->用戶空間兩者之間需要大量數據傳輸等操作的話效率是非常高的。
?
?
原理
首先,“映射”這個詞,就和數學課上說的“一一映射”是一個意思,就是建立一種一一對應關系,在這里主要是只?硬盤上文件?的位置與進程?邏輯地址空間?中一塊大小相同的區域之間的一一對應,如圖1中過程1所示。這種對應關系純屬是邏輯上的概念,物理上是不存在的,原因是進程的邏輯地址空間本身就是不存在的。在內存映射的過程中,并沒有實際的數據拷貝,文件沒有被載入內存,只是邏輯上被放入了內存,具體到代碼,就是建立并初始化了相關的數據結構(struct?address_space),這個過程有系統調用mmap()實現,所以建立內存映射的效率很高。
?
圖1.內存映射原理??
?
?
?
?
既然建立內存映射沒有進行實際的數據拷貝,那么進程又怎么能最終直接通過內存操作訪問到硬盤上的文件呢?那就要看內存映射之后的幾個相關的過程了。
?
mmap()會返回一個指針ptr,它指向進程邏輯地址空間中的一個地址,這樣以后,進程無需再調用read或write對文件進行讀寫,而只需要通過ptr就能夠操作文件。但是ptr所指向的是一個邏輯地址,要操作其中的數據,必須通過MMU將邏輯地址轉換成物理地址,如圖1中過程2所示。這個過程與內存映射無關。
?
前面講過,建立內存映射并沒有實際拷貝數據,這時,MMU在地址映射表中是無法找到與ptr相對應的物理地址的,也就是MMU失敗,將產生一個缺頁中斷,缺頁中斷的中斷響應函數會在swap中尋找相對應的頁面,如果找不到(也就是該文件從來沒有被讀入內存的情況),則會通過mmap()建立的映射關系,從硬盤上將文件讀取到物理內存中,如圖1中過程3所示。這個過程與內存映射無關。
?
如果在拷貝數據時,發現物理內存不夠用,則會通過虛擬內存機制(swap)將暫時不用的物理頁面交換到硬盤上,如圖1中過程4所示。這個過程也與內存映射無關。
?
?
效率
?
從代碼層面上看,從硬盤上將文件讀入內存,都要經過文件系統進行數據拷貝,并且數據拷貝操作是由文件系統和硬件驅動實現的,理論上來說,拷貝數據的效率是一樣的。但是通過內存映射的方法訪問硬盤上的文件,效率要比read和write系統調用高,這是為什么呢?原因是read()是系統調用,其中進行了數據拷貝,它首先將文件內容從硬盤拷貝到內核空間的一個緩沖區,如圖2中過程1,然后再將這些數據拷貝到用戶空間,如圖2中過程2,在這個過程中,實際上完成了?兩次數據拷貝?;而mmap()也是系統調用,如前所述,mmap()中沒有進行數據拷貝,真正的數據拷貝是在缺頁中斷處理時進行的,由于mmap()將文件直接映射到用戶空間,所以中斷處理函數根據這個映射關系,直接將文件從硬盤拷貝到用戶空間,只進行了?一次數據拷貝?。因此,內存映射的效率要比read/write效率高。
?
?
圖2.read系統調用原理
?
下面這個程序,通過read和mmap兩種方法分別對硬盤上一個名為“mmap_test”的文件進行操作,文件中存有10000個整數,程序兩次使用不同的方法將它們讀出,加1,再寫回硬盤。通過對比可以看出,read消耗的時間將近是mmap的兩到三倍。
?
1 #include<unistd.h> 2 3 #include<stdio.h> 4 5 #include<stdlib.h> 6 7 #include<string.h> 8 9 #include<sys/types.h> 10 11 #include<sys/stat.h> 12 13 #include<sys/time.h> 14 15 #include<fcntl.h> 16 17 #include<sys/mman.h> 18 19 20 21 #define MAX 10000 22 23 24 25 int main() 26 27 { 28 29 int i=0; 30 31 int count=0, fd=0; 32 33 struct timeval tv1, tv2; 34 35 int *array = (int *)malloc( sizeof(int)*MAX ); 36 37 38 39 /*read*/ 40 41 42 43 gettimeofday( &tv1, NULL ); 44 45 fd = open( "mmap_test", O_RDWR ); 46 47 if( sizeof(int)*MAX != read( fd, (void *)array, sizeof(int)*MAX ) ) 48 49 { 50 51 printf( "Reading data failed.../n" ); 52 53 return -1; 54 55 } 56 57 for( i=0; i<MAX; ++i ) 58 59 60 61 ++array[ i ]; 62 63 if( sizeof(int)*MAX != write( fd, (void *)array, sizeof(int)*MAX ) ) 64 65 { 66 67 printf( "Writing data failed.../n" ); 68 69 return -1; 70 71 } 72 73 free( array ); 74 75 close( fd ); 76 77 gettimeofday( &tv2, NULL ); 78 79 printf( "Time of read/write: %dms/n", tv2.tv_usec-tv1.tv_usec ); 80 81 82 83 /*mmap*/ 84 85 86 87 gettimeofday( &tv1, NULL ); 88 89 fd = open( "mmap_test", O_RDWR ); 90 91 array = mmap( NULL, sizeof(int)*MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ); 92 93 for( i=0; i<MAX; ++i ) 94 95 96 97 ++array[ i ]; 98 99 munmap( array, sizeof(int)*MAX ); 100 101 msync( array, sizeof(int)*MAX, MS_SYNC ); 102 103 free( array ); 104 105 close( fd ); 106 107 gettimeofday( &tv2, NULL ); 108 109 printf( "Time of mmap: %dms/n", tv2.tv_usec-tv1.tv_usec ); 110 111 112 113 return 0; 114 115 }?
輸出結果:
Time of read/write: 154msTime of mmap: 68ms?
轉載于:https://www.cnblogs.com/wanpengcoder/articles/5306688.html
總結
以上是生活随笔為你收集整理的[转载]linux内存映射mmap原理分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实现对数组找最大最小数
- 下一篇: dos常用文件操作命令