一直都對(duì)內(nèi)存映射文件這個(gè)概念很模糊缺猛,不知道它和虛擬內(nèi)存有什么區(qū)別拷沸,而且映射這個(gè)詞也很讓人迷茫,今天終于搞清楚了消玄。跟伏。丢胚。下面,我先解釋一下我對(duì)映射這個(gè)詞的理解受扳,再區(qū)分一下幾個(gè)容易混淆的概念携龟,之后,什么是內(nèi)存映射就很明朗了勘高。
原理
首先峡蟋,“映射”這個(gè)詞,就和數(shù)學(xué)課上說的“一一映射”是一個(gè)意思华望,就是建立一種一一對(duì)應(yīng)關(guān)系蕊蝗,在這里主要是只?硬盤上文件?的位置與進(jìn)程?邏輯地址空間?中一塊大小相同的區(qū)域之間的一一對(duì)應(yīng),如圖1中過程1所示立美。這種對(duì)應(yīng)關(guān)系純屬是邏輯上的概念匿又,物理上是不存在的,原因是進(jìn)程的邏輯地址空間本身就是不存在的建蹄。在內(nèi)存映射的過程中碌更,并沒有實(shí)際的數(shù)據(jù)拷貝,文件沒有被載入內(nèi)存洞慎,只是邏輯上被放入了內(nèi)存痛单,具體到代碼,就是建立并初始化了相關(guān)的數(shù)據(jù)結(jié)構(gòu)(struct?address_space)劲腿,這個(gè)過程有系統(tǒng)調(diào)用mmap()實(shí)現(xiàn)旭绒,所以建立內(nèi)存映射的效率很高。
既然建立內(nèi)存映射沒有進(jìn)行實(shí)際的數(shù)據(jù)拷貝焦人,那么進(jìn)程又怎么能最終直接通過內(nèi)存操作訪問到硬盤上的文件呢挥吵?那就要看內(nèi)存映射之后的幾個(gè)相關(guān)的過程了。
mmap()會(huì)返回一個(gè)指針ptr花椭,它指向進(jìn)程邏輯地址空間中的一個(gè)地址忽匈,這樣以后,進(jìn)程無需再調(diào)用read或write對(duì)文件進(jìn)行讀寫矿辽,而只需要通過ptr就能夠操作文件丹允。但是ptr所指向的是一個(gè)邏輯地址,要操作其中的數(shù)據(jù)袋倔,必須通過MMU將邏輯地址轉(zhuǎn)換成物理地址雕蔽,如圖1中過程2所示。這個(gè)過程與內(nèi)存映射無關(guān)宾娜。?
前面講過批狐,建立內(nèi)存映射并沒有實(shí)際拷貝數(shù)據(jù),這時(shí)前塔,MMU在地址映射表中是無法找到與ptr相對(duì)應(yīng)的物理地址的贾陷,也就是MMU失敗缘眶,將產(chǎn)生一個(gè)缺頁(yè)中斷,缺頁(yè)中斷的中斷響應(yīng)函數(shù)會(huì)在swap中尋找相對(duì)應(yīng)的頁(yè)面髓废,如果找不到(也就是該文件從來沒有被讀入內(nèi)存的情況),則會(huì)通過mmap()建立的映射關(guān)系该抒,從硬盤上將文件讀取到物理內(nèi)存中慌洪,如圖1中過程3所示。這個(gè)過程與內(nèi)存映射無關(guān)凑保。
如果在拷貝數(shù)據(jù)時(shí)冈爹,發(fā)現(xiàn)物理內(nèi)存不夠用,則會(huì)通過虛擬內(nèi)存機(jī)制(swap)將暫時(shí)不用的物理頁(yè)面交換到硬盤上欧引,如圖1中過程4所示频伤。這個(gè)過程也與內(nèi)存映射無關(guān)。
效率
從代碼層面上看芝此,從硬盤上將文件讀入內(nèi)存憋肖,都要經(jīng)過文件系統(tǒng)進(jìn)行數(shù)據(jù)拷貝,并且數(shù)據(jù)拷貝操作是由文件系統(tǒng)和硬件驅(qū)動(dòng)實(shí)現(xiàn)的婚苹,理論上來說岸更,拷貝數(shù)據(jù)的效率是一樣的。但是通過內(nèi)存映射的方法訪問硬盤上的文件膊升,效率要比read和write系統(tǒng)調(diào)用高怎炊,這是為什么呢?原因是read()是系統(tǒng)調(diào)用廓译,其中進(jìn)行了數(shù)據(jù)拷貝评肆,它首先將文件內(nèi)容從硬盤拷貝到內(nèi)核空間的一個(gè)緩沖區(qū),如圖2中過程1非区,然后再將這些數(shù)據(jù)拷貝到用戶空間瓜挽,如圖2中過程2,在這個(gè)過程中院仿,實(shí)際上完成了?兩次數(shù)據(jù)拷貝?秸抚;
而mmap()也是系統(tǒng)調(diào)用,如前所述歹垫,mmap()中沒有進(jìn)行數(shù)據(jù)拷貝剥汤,真正的數(shù)據(jù)拷貝是在缺頁(yè)中斷處理時(shí)進(jìn)行的,由于mmap()將文件直接映射到用戶空間排惨,所以中斷處理函數(shù)根據(jù)這個(gè)映射關(guān)系吭敢,直接將文件從硬盤拷貝到用戶空間,只進(jìn)行了?一次數(shù)據(jù)拷貝?暮芭。因此鹿驼,內(nèi)存映射的效率要比read/write效率高欲低。
下面這個(gè)程序,通過read和mmap兩種方法分別對(duì)硬盤上一個(gè)名為“mmap_test”的文件進(jìn)行操作畜晰,文件中存有10000個(gè)整數(shù)砾莱,程序兩次使用不同的方法將它們讀出,加1凄鼻,再寫回硬盤腊瑟。通過對(duì)比可以看出,read消耗的時(shí)間將近是mmap的兩到三倍块蚌。
WR?);
if(sizeof(int)*MAX?!=?read(?fd,?(void*)array,sizeof(int)*MAX?)?)
{
printf("Reading?data?failed.../n");
return-1;
}
for(?i=0;?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
++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?);
return0;
}
輸出結(jié)果:
Timeofread/write:154ms
Timeofmmap:68ms
V ? ? X ? 獲?取?更 多 精彩 內(nèi)容