問(wèn)題一、Mapping有“映射”之意,那么在該語(yǔ)境中形成映射關(guān)系的雙方是誰(shuí),也就是從哪里映射到哪里呢?
要回答這個(gè)問(wèn)題,我們必須要對虛擬內存有所了解?,F在操作系統中,大多都使用虛擬內存技術(shù)來(lái)對內存進(jìn)行管理。通過(guò)虛擬內存,操作系統給予了每個(gè)進(jìn)程一個(gè)統一的地址空間。在32位操作系統中,該地址空間的大小達到 2^32個(gè),也就是4G了。從一個(gè)進(jìn)程的角度看來(lái),這4G的地址空間是自己獨享的,也就是說(shuō),如果操作系統允許的話(huà),我可以訪(fǎng)問(wèn)這4G地址空間中的任何一個(gè)。當然,操作系統是不可能讓一個(gè)進(jìn)程隨心所欲地使用這些地址的。下面,我們來(lái)看看這些地址具體是怎樣分配的:

上面這個(gè)圖大家應該都很熟悉,它是Linux中進(jìn)程的內存映象。我們可以看到,在4G的地址空間中,我們先從下往上看, 0~0x08047ffff(大概128M左右)是系統保留的,不能使用。read-only segment和read/write segment用以存放系統加載器從可執行文件中載入的代碼段以及數據段等內容。運行時(shí)堆大家應該都比較清楚,是動(dòng)態(tài)分配內存的地方,我們通過(guò)malloc和free等函數動(dòng)態(tài)在堆中分配和釋放內存,堆的大小是往上增長(cháng)的,最大可達到0x3FFFFFFF處。好,到這里我們在從上往下看,0xc0000000以上是核心虛擬內存,專(zhuān)門(mén)為操作系統核心的數據結構以及代碼預留的,一般用戶(hù)進(jìn)程無(wú)權使用。然后就到了棧區了,這里是系統保存跟函數操作有關(guān)的數據,如局部變量,函數參數等內容。與堆不一樣,棧是從上往下增長(cháng)的,其棧頂通過(guò)寄存器esp指出。那么被堆和棧夾著(zhù)的區域是干什么的呢?原來(lái),那是用來(lái)放動(dòng)態(tài)共享庫的。動(dòng)態(tài)共享庫是在程序被載入時(shí)或者運行過(guò)程中載入到進(jìn)程內存空間中的,它存放的地方就是我們稱(chēng)作內存映射區的這個(gè)地方。
這樣一看,原來(lái)進(jìn)程開(kāi)始運行時(shí),4G的地址已經(jīng)被用掉了不少,其中,光是操作系統所占用的核心虛擬內存就達到1G,加上程序的代碼和數據以及動(dòng)態(tài)共享庫等等,我們大概就剩下2G左右的地址空間可以使用了。那么,這2G空間我們是如何使用的呢?第一,我們使用malloc函數,在堆中分配空間,使堆往上增長(cháng);第二,我們在函數中使用局部的數據,以及函數調用時(shí)現場(chǎng)的保留,使??臻g往下增長(cháng);第三,我們使用File Mapping,使內存映射區往上增長(cháng)。
好了,終于出現File Mapping 了?,F在,我們也可以知道題目中“映射”的其中一方了:內存。原來(lái)它就是在內存映射區中的一段地址空間。那么,“映射”的另一方又是什么呢?那自然是文件了。我們可以將任何類(lèi)型任何大?。ㄖ灰僮飨到y支持,現在win32支持最大的文件為16EB,就是2^64)的文件映射到內存映射區中。當然,太大的文件我們不可能一次性把它全部映射到虛擬內存中去,畢竟我們大概只有2G的地址空間,兩者間是不可能構成一一對應的關(guān)系的。此時(shí),我們可以將文件分段進(jìn)行映射,每次將文件的一部分映射到內存空間中。映射完以后,我們就可以像訪(fǎng)問(wèn)內存那樣直接訪(fǎng)問(wèn)文件了。
問(wèn)題二、數據在哪呢?數據文件?物理內存?頁(yè)面文件?
這里,我們暫且將被映射的文件稱(chēng)為數據文件。當我們映射好一個(gè)數據文件以后,操作系統并不會(huì )馬上將文件中的內容提交到物理內存中去,數據還是原封不動(dòng)地放在數據文件中。但是,當程序首次對文件中某個(gè)數據進(jìn)行訪(fǎng)問(wèn)時(shí)(read /write),操作系統就會(huì )將該數據從數據文件中調入物理內存中,供CPU使用。操作完畢后,當我們解除映射時(shí),操作系統將根據映射的屬性(write/write-on-copy)決定是將更改后的數據寫(xiě)回到數據文件中還是將更改直接丟棄。Readonly 不存在這個(gè)問(wèn)題,因為不可能被更改,因此unmap時(shí)只需將內存中的數據丟棄就可以了。
這中間還有一個(gè)問(wèn)題,那就是在映射以后和解除映射之前這個(gè)時(shí)間段內,物理內存中的數據是有可能被換出的(swap out),那么,換出時(shí)這些數據是被存放在數據文件中還是像一般數據那樣存放在系統的頁(yè)面文件中呢?同樣,這也是跟映射的屬性緊密相關(guān)的:
如果映射為readonly,那么換出時(shí)只需修改相應的頁(yè)表(page table)內容,標注其已被換出即可。
如果映射為write-on-copy,那么換出將存放在頁(yè)面文件中,
如果映射為write,那么換出時(shí)將寫(xiě)會(huì )到數據文件中。
問(wèn)題三、使用File Mapping為什么可以提高訪(fǎng)問(wèn)文件的速度呢?
這是因為操作系統在處理一般讀寫(xiě)跟處理內存映射使用的方法不一樣。在處理一般的讀寫(xiě)操作時(shí),操作系統一般使用中斷的方式,先將內容拷貝到核心虛擬內存緩沖,然后再拷貝到進(jìn)程空間中;但是,處理內存映射文件時(shí),一般使用虛擬內存管理器,無(wú)需進(jìn)行中間的拷貝過(guò)程,因此速度加快。此外,像Windows這樣使用頁(yè)式管理虛擬內存的操作系統中,數據的換入換出都是以頁(yè)為單位的(通常是4k或者8k),因為程序一般都具有時(shí)間和空間的局部性(locality),因此,相當于進(jìn)行了大量的緩沖操作,有利于提高性能。
問(wèn)題四、什么情況適合使用 File Mapping呢?看看人家的建議:
File mapping is effective in the following situations:
You have a large file whose contents you want to access randomly one or more times.
You have a small file whose contents you want to read into memory all at once and access frequently. This technique is best for files that are no more than a few virtual memory pages in size.
問(wèn)題五、為什么在操作大文件時(shí)速度變得很慢呢? 遇到這個(gè)問(wèn)題,你可以首先打開(kāi)Windows的任務(wù)管理器,看看你進(jìn)程究竟使用了多少的內存。呵呵,通常都是個(gè)天文數字。占用了那么多的內存,系統肯定就很慢了。遇到這樣的問(wèn)題,我們通常都是使用內存映射文件對數據文件進(jìn)行遍歷操作,譬如像將A文件拷貝為B文件。上面我們提到,操作系統是在真正用到數據的時(shí)候才會(huì )把它從數據文件中提交到物理內存里面的,因此,剛做好映射不進(jìn)行操作的話(huà),進(jìn)程并不會(huì )消耗多少內存。但是,一旦你開(kāi)始進(jìn)行遍歷,那么,操作系統就馬上將它們調入物理內存中(你可以看看頁(yè)面錯誤的數量,肯定是飛速增長(cháng)的),于是,內存就一路飛漲了。 怎么辦呢?不要一次性把整個(gè)文件進(jìn)行映射,而是分開(kāi)進(jìn)行,操作完一部分后,將它unmap掉,這樣,操作系統就會(huì )把它們“趕回家去”了,內存就不會(huì )占用太高了。Reference:[1] Computer Systems:A Programmer's Perspective ,Chapter 10 Virtual Memory [2] Memory Management
[3]Virtual Memory and Memory Mapping
[4] Mapping Files Into Memory
聯(lián)系客服