轉自:http://blog.csdn.net/yusiguyuan/article/details/23388771
?
內存映射,簡而言之就是將用戶空間的一段內存區域映射到內核空間,映射成功后,用戶對這段內存區域的修改可以直接反映到內核空間,同樣,內核空間對這段區域的修改也直接反映用戶空間。那么對于內核空間<---->用戶空間兩者之間需要大量數據傳輸等操作的話效率是非常高的。
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
?