當前位置:
首頁 > 知識 > Linux內存映射mmap原理分析

Linux內存映射mmap原理分析


來自:Joe James


鏈接:https://blog.csdn.net/joejames/article/details/37958017




一直都對內存映射文件這個概念很模糊,不知道它和虛擬內存有什麼區別,而且映射這個詞也很讓人迷茫,今天終於搞清楚了。。。下面,我先解釋一下我對映射這個詞的理解,再區分一下幾個容易混淆的概念,之後,什麼是內存映射就很明朗了。


原理


首先,「映射」這個詞,就和數學課上說的「一一映射」是一個意思,就是建立一種一一對應關係,在這裡主要是只 

硬碟上文件

 的位置與進程 

邏輯地址空間

 中一塊大小相同的區域之間的一一對應,如圖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的兩到三倍。



#

include

<unistd.h>


#

include

<stdio.h>


#

include

<stdlib.h>


#

include

<string.h>


#

include

<sys/types.h>


#

include

<sys/stat.h>


#

include

<sys/time.h>


#

include

<fcntl.h>


#

include

<sys/mman.h>

#

define

 MAX 10000

int

 

main

()


{

int

 i=

0

;

int

 count=

0

, fd=

0

;

struct

 

timeval

 

tv1

tv2

;


int

 *

array

 = (

int

 *)

malloc

sizeof

(

int

)*MAX );

/*read*/

gettimeofday( &tv1, 

NULL

 );
fd = open( 

"mmap_test"

, O_RDWR );

if

sizeof

(

int

)*MAX != read( fd, (

void

 *)

array

sizeof

(

int

)*MAX ) )
{

printf

"Reading data failed.../n"

 );

return

 

-1

;
}

for

( i=

0

; i<MAX; ++i )

++

array

[ i ];

if

sizeof

(

int

)*MAX != write( fd, (

void

 *)

array

sizeof

(

int

)*MAX ) )
{

printf

"Writing data failed.../n"

 );

return

 

-1

;
}

free

array

 );
close( fd );
gettimeofday( &tv2, 

NULL

 );

printf

"Time of read/write: %dms/n"

, tv2.tv_usec-tv1.tv_usec );

/*mmap*/

gettimeofday( &tv1, 

NULL

 );
fd = open( 

"mmap_test"

, O_RDWR );

array

 = mmap( 

NULL

sizeof

(

int

)*MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 

0

 );

for

( i=

0

; i<MAX; ++i )

++

array

[ i ];
munmap( 

array

sizeof

(

int

)*MAX );
msync( 

array

sizeof

(

int

)*MAX, MS_SYNC );

free

array

 );
close( fd );
gettimeofday( &tv2, 

NULL

 );

printf

"Time of mmap: %dms/n"

, tv2.tv_usec-tv1.tv_usec );

return

 

0

;
}



輸出結果:



Time 

of

 

read

/

write

154

ms
Time 

of

 mmap: 

68

ms



●編號656,輸入編號直達本文



●輸入m獲取文章

目錄

推薦↓↓↓



運維


更多推薦

25個技術類公眾微信


涵蓋:程序人生、演算法與數據結構、黑客技術與網路安全、大數據技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Linux學習 的精彩文章:

從微觀角度來看Linux內核設計
linux.org被黑客攻擊了,或因對Linux行為準則不滿

TAG:Linux學習 |