記憶體對應檔剖析(二)

來源:互聯網
上載者:User
原文:http://dev.csdn.net/article/14/14321.shtm

前面已經提到:記憶體對應檔是拿檔案直接當作系統的記憶體使用量,那麼它主要

的用途是什麼呢?主要有以下兩點:

1.       直接用記憶體對應檔來訪問磁碟上的資料檔案,無需再進行檔案

的I/0操作.

2.       用來在多個進程之間共用資料.進程間共用資料有很多種方法,比如

發送訊息WM_COPYDATA,匿名管道等等,但他們的低層都毫無例外

的使用到了Mapping File.然而因為WM_COPYDATA一定需要使用

同步函數SendMessage,所以在即時性方面表現的不是很好.

(至於同步和非同步區別可以參考筆者的另一篇文章:

http://www.csdn.net/Develop/read_article.asp?id=14204)

 

前面已經提到過,記憶體對應檔的位置在3G—4G的空間中,這部分是Win32

所有進程都看的到並且共用的,自然可以用來傳輸資料,另外各個進程所

共用的DLL等也是映射在這個空間範圍.

   記憶體對應檔的使用可以分為以下三步:

1.CreateFileMapping    建立一個檔案對應核心對象

2.MapViewOfFile       將檔案資料對應進進程地址空間

3.UnmapViewOfFile     從進程地址空間解除這個映射

 

   下面以Mapping File的兩個主要作用分別給出兩個簡單的例子:

 

A 直接用記憶體對應檔訪問檔案.

    首先在C盤下建立一個Mapping.txt裡面輸入1234567

    HANDLE hFile=CreateFile("c:\\mapping.txt",

                                GENERIC_READ|GENERIC_WRITE,

                                FILE_SHARE_READ|FILE_SHARE_WRITE,

                                NULL,

                                OPEN_EXISTING,

                                FILE_ATTRIBUTE_NORMAL,

                                NULL);

    HANDLE hFilemap = CreateFileMapping(hFile..);

                                          NULL,

                                          PAGE_READWRITE,

                                          0,

                                          100,     // 只是開闢100個

                                          NULL);

LPVOID pVoid=MapViewOfFile(hFilemap,FILE_MAP_ALL_ACCESS,0,0,0);

Char *Buf=(char *)pVoid;

Buf[0]=”T”

CloseHandle(hFile);

CloseHandle(hFilemap);

UnmapViewOfFile(pVoid);

(注意:沒有考慮異常情況)

 

       這樣,當我們再開啟Mapping.txt的檔案的時候,就發現第一個位元組”1”

    已經被改為了’T’.

   也許有些讀者會提問:幹嗎這麼麻煩呢?直接用fopen或者CreateFile

不就OK了?是的,小檔案是,可是如果這個檔案有上百兆呢?Mapping

File為我們提供了一種直接映射存取的方便之道.

   這裡有個小小的地方要注意,建立映射對象的時候有個保護屬性

fdwProtect可以選擇PAGE_WRITECOPY,顧名思義是用來寫拷貝的,

系統在收到這個參數後,將會從分頁檔中額外的提交實體記憶體

(前面已經提到過,映射對象不使用分頁檔).當發生讀操作的時候,系統

仍舊使用對應檔,當發生寫操作的時候,系統從分頁檔中分配頁面,

從對應檔中拷貝到該頁進行訪問,這樣使得原先的寫操作被丟棄.

讀者可以試著照上面的例子把CreateFileMapping和MapViewOfFile

裡面的兩個對應位元組改為PAGE_WRITECOPY和FILE_MAP_COPY,

這樣原檔案即使有寫操作也不會被改動.

 

B 在不同的進程間共用資料

    要進行共用如果每次都要在硬碟上建立一個檔案該是多麼的麻煩啊,

 Windows提供了這樣一種機制:當在建立映射對象的時候如果hFile

 填上(HANDLE)0xFFFFFFFF,系統會自動從分頁檔中建立檔案對象.

    另外有書上提到共用方式是以p2p的方式還是c/s的架構來進行,

 我想不過是開啟的方式不同吧,沒有別的差別,(一個用CreateFileMapping

開啟看是否為已經存在,另一個用OpenFileMapping開啟)

 

來看個例子;

 

 # define WM_DATACOMING    WM_USER+100

 

進程A:

 HANDLE hFilemap=CreateFileMapping((HANDLE)0xFFFFFFFF,

                                          NULL,

                                          PAGE_READWRITE,

                                          0,

                                          100,

                                          "SHARED");

 

 LPVOID pVoid=MapViewOfFile(hFilemap,FILE_MAP_ALL_ACCESS,0,0,0);

 memset(pVoid,0,100);

 strcpy((char *)pVoid,"this is a mapping file test");

HANDLE hDes=FindWindow(NULL,"MAPPING");   // 對象視窗的名稱

SendMessage(hDes, WM_DATACOMING,0,0);

CloseHandle(hFilemap);

UnmapViewOfFile(pVoid);

 

進程B(擁有視窗名稱為MAPPING)

 

//  WM_DATACOMING訊息捕捉函數

HANDLE hFilemap=OpenFileMapping(NULL,NULL,"SHARED");

LPVOID pVoid=MapViewOfFile(hFilemap,FILE_MAP_ALL_ACCESS,0,0,0);

Label1->Caption=(char *)pVoid;

 

可以看到資料已經被正確的傳送過來.

可能有些讀者已經注意到,在這種情況下需要給映射對象取個名字(例子

中為SHARED),是的,在這種用途下需要給它取個名字,而在第一種應用

中這個地方可以被忽略.這裡可能會引起打架的地方就是這個名字了,

如果多個進程建立了多個映射對象,根據名字來不是比較容易衝突了嗎?

是的,這是個問題,筆者建議可以採用表單的名稱(MAPPING)或者別的

唯一的ID來使得不引起混淆.

   請注意這個函數:MapViewOfFile,注意到裡面有個單詞:Viewà視

這個函數是把建立好的映射對象真正提交到地址空間去,這就產生了

一個視.Windows中允許映射統一資料檔案的多個視,比如說可以將

一個檔案的全部映射到一個視,然後將他的前10K單獨映射為一個視.

那麼系統是不是真正區別這多個視呢?答案是要看是什麼系統,

如果是Win9x,系統並沒有額外再映射一個新的地址給它,而只是

把原先的基地址加上一個位移量做為新的視的地址而返回,換句話

說地址空間只有一份,而WinNT則是真正的新產生了一個地址空間

返回來.

   看看下面這個小例子:

HANDLE hFilemap=CreateFileMapping((HANDLE)0xFFFFFFFF,

                                          NULL,

                                          PAGE_READWRITE,

                                          0,

                                          100,

                                          "SHARED");

// 提交整個地址給空間

LPVOID pVoid1=MapViewOfFile(hFilemap,FILE_MAP_ALL_ACCESS,0,0,0);

// 從位移40產生一個新視

LPVOID pVoid2= MapViewOfFile(hFilemap,FILE_MAP_ALL_ACCESS,0,40,0);

If(pVoid1+40==pVoid2)

       MessageBox(“Run On Win95”);

else

 MessageBox(“Run On NT”);

 

 可以注意到返回的值為0x8…這符合地址清單中MappingFile的位置.必然在

“Server”中開啟的映射對象的地址和”Client”中利用MapViewOfFile返回的地址

是一致的(9x環境).這也是因為這個部分的地址空間是大家共用的.

   那麼既然是一樣的,能不能直接使用這個值呢?比如上面的進程間共用資料

的例子:如果進程A的發送語句改為:

   // 把指標值作為參數傳遞

   SendMessage(hDes, WM_DATACOMING,(WPARAM) pVoid,0);

 

進程B的接受訊息部分改為:

   LPVOID pVoid=(LPVOID)MSG.WPARAM;

   Label1->Caption=(char *)pVoid;

 

可以看到可以正確的顯示出來,因為指標所指的地方的確是有這麼一筆資料,

那麼是不是意味著我們就能這麼使用呢?答案是否定的,首先這個值相等

只是在Win9x的環境下,在NT環境下是不相等的,另外NT下訪問這個地址

空間的時候要求一定要先使用MapViewOfFile函數.這是第一個原因,更加

重要的是記憶體映射對象屬於核心對象(Kernal Object),這種對象的最大不同就

在於它是系統維護的一塊資料結構,使用者只能通過相應的介面函數進行間接

的訪問.每訪問一次就增加一個引用記數(reference count),當計數器變為0的

時候,系統自動釋放這個核心對象.在上面的例子中,儘管Server端和Client

的值是一樣的,但是如果Server端執行UnmapViewOfFile釋放核心對象的時候,

這部分資料將會被系統釋放掉,因為它的引用計數只是1,只有我們在Client

端使用MapViewOfFile增加這個對象的計數的時候,才不會被系統釋放掉.

 

                                                    堆

 Win32的堆位於進程私人空間內,屬於自由分配區,比如大家在C++中常

使用的new操作符就是在這個地方分配的,關於堆的操作有HeapCreate

和HeapAlloc等,這裡就不再繼續討論了.

 

後記

   Mapping File一直是個比較難以討論的問題,在CSDN上也看到不少網友

討論的比較模糊,最後不了了之.筆者對這個問題也一直想搞個明白,在看

Richter的大作<Advanced Windows>的時候,因為記憶體這個部分是連在一起

的好幾章,理論也比較抽象和繁雜,看的很是頭痛.寫出這篇文章也是希望

協助大家更好的理解這個部分,對記憶體有著進一步的瞭解以便更好的開發程式.

有興趣進一步研究者可聯絡 QQ:33854303

 

 

                                                                                xrbeck寫於2002/7/3

參考資料:

1.       Windows 進階編程指南 Jeffery Richter

2.       Windows程式設計      Charles Petzold

3.       Win32多線程程式設計 侯捷譯

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.