天天看點

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

17.3 使用記憶體映射檔案

若要使用記憶體映射檔案,必須執行下列操作步驟:

1) 建立或打開一個檔案核心對象,該對象用于辨別磁盤上你想用作記憶體映射檔案的檔案。

2) 建立一個檔案映射核心對象,告訴系統該檔案的大小和你打算如何通路該檔案。

3) 讓系統将檔案映射對象的全部或一部分映射到你的程序位址空間中。

當完成對記憶體映射檔案的使用時,必須執行下面這些步驟将它清除:

1) 告訴系統從你的程序的位址空間中撤消檔案映射核心對象的映像。

2) 關閉檔案映射核心對象。

3) 關閉檔案核心對象。

下面将詳細介紹這些操作步驟。

17.3.1 步驟1:建立或打開檔案核心對象

C r e a t e F i l e函數:

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

函數擁有好幾個參數。這裡隻重點介紹前 3個參數,即p s z F i l e N a m e,d w D e s i r e dA c c e s s和d w S h a r e M o d e。

你可能會猜到,第一個參數 p s z F i l e N a m e用于指明要建立或打開的檔案的名字(包括一個選項路徑)。第二個參數d w D e s i r e d A c c e s s用于設定如何通路該檔案的内容。可以設定表 1 7 - 3所列的4個值中的一個。

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

    當建立或打開一個檔案,将它作為一個記憶體映射檔案來使用時,請標明最有意義的一個或

多個通路标志,以說明你打算如何通路檔案的資料。對記憶體映射檔案來說,必須打開用于隻讀通路或讀寫通路的檔案,是以,可以分别設定 G E N E R I C _ R E A D或GENERIC_READ  |G E N E R I C _ W R I T E。

d w S h a r e M o d e告訴系統你想如何共享該檔案。可以為 d w S h a r e M o d e設定表1 7 - 4所列的4個值之一。

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

C r e a t e F i l e函數成功地建立或打開指定的檔案,便傳回一個檔案核心對象的句柄,否則傳回I N VA L I D _ H A N D L E _ VA L U E。

注意 能夠傳回句柄的大多數Wi n d o w s函數如果運作失敗,那麼就會傳回N U L L。但是,C r e a t e F i l e函數将傳回I N VA L I D _ H A N D L E _ VA L U E,它定義為((H A N D L E)- 1)。

17.3.2 步驟2:建立一個檔案映射核心對象

C r e a t e F i l e函數,就可以将檔案映像的實體存儲器的位置告訴作業系統。你傳遞的路徑名用于指明支援檔案映像的實體存儲器在磁盤(或網絡或CD光牒)上的确切位置。這時,必須告訴系統,檔案映射對象需要多少實體存儲器。若要進行這項操作,可以調用 C r e a t e F i l e M a p p i n g函數:

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

h F i l e用于辨別你想要映射到程序位址空間中的檔案句柄。該句柄由前面調用的C r e a t e F i l e函數傳回。p s a參數是指向檔案映射核心對象的S E C U R I T Y _ AT T R I B U T E S結構的指針,通常傳遞的值是N U L L(它提供預設的安全特性,傳回的句柄是不能繼承的)。

    本章開頭講過,建立記憶體映射檔案就像保留一個位址空間區域然後将實體存儲器送出給該區域一樣。因為記憶體映射檔案的實體存儲器來自磁盤上的一個檔案,而不是來自從系統的頁檔案中配置設定的空間。當建立一個檔案映射對象時,系統并不為它保留位址空間區域,也不将檔案的存儲器映射到該區域(下一節将介紹如何進行這項操作)。但是,當系統将存儲器映射到程序的位址空間中去時,系統必須知道應該将什麼保護屬性賦予實體存儲器的頁面。

C r e a t e F i l e M a p p i n g函數的f d w P r o t e c t參數使你能夠設定這些保護屬性。大多數情況下,可以設定表1 7 - 5中列出的3個保護屬性之一。

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

在Windows 98下,可以将PA G E _ W R I T E C O P Y标志傳遞給C r e a t e F i l eM a p p i n g,這将告訴系統從頁檔案中送出存儲器。該頁檔案存儲器是為資料檔案的資料拷貝保留的,隻有修改過的頁面才被寫入頁檔案。你對該檔案的資料所作的任何修

改都不會重新填入原始資料檔案。其最終結果是, PA G E _ W R I T E C O P Y标志的作用在Windows 2000和Windows 98上是相同的。

4個節保護屬性,你可以用 O R将它們連接配接起來放入C r e a t e F i l e M a p p i n g函數的f d w P r o t e c t參數中。節隻是用于記憶體映射的另一個術語。

S E C _ N O C A C H E,它告訴系統,沒有将檔案的任何記憶體映射頁面放入高速緩存。是以,當将資料寫入該檔案時,系統将更加經常地更新磁盤上的檔案資料。這個标志與PA G E _ N O C A C H E保護屬性标志一樣,是供裝置驅動程式開發人員使用的,應用程式通常不使用。

Windows 98 Windows 98将忽略S E C _ N O C A C H E标志。

S E C _ I M A G E,它告訴系統,你映射的檔案是個可移植的可執行(P E)檔案映像。當系統将該檔案映射到你的程序的位址空間中時,系統要檢視檔案的内容,以确定将哪些保護屬性賦予檔案映像的各個頁面。例如, P E檔案的代碼節( . t e x t)通常用PA G E _ E X E C U T E _ R E A D 屬 性進 行映 射, 而 P E文 件的 資料 節 ( . d a t a ) 則通 常用PA G E _ R E A D W R I T E屬性進行映射。如果設定的屬性是 S E C _ I M A G E,則告訴系統進行檔案映像的映射,并設定相應的頁面保護屬性。

将忽略S E C _ I M A G E标志。

S E C _ R E S E RV E和S E C _ C O M M I T,它們是兩個互斥屬性,當使用記憶體映射資料檔案時,它們不能使用。這兩個标志将在本章後面介紹。當建立記憶體映射資料檔案時,不應該設定這些标志中的任何一個标志。C r e a t e F i l e M a p p i n g将忽略這些标志。

的另外兩個參數是d w M a x i m u m S i z e H i g h和d w M a x i m u m S i z e L o w,它們是兩個最重要的參數。C r e a t e F i l e M a p p i n g函數的主要作用是保證檔案映射對象能夠得到足夠的實體存儲器。這兩個參數将告訴系統該檔案的最大位元組數。它需要兩個 3 2位的值,因為Wi n d o w s支援的檔案大小可以用 6 4位的值來表示。d w M a x i m u m S i z e H i g h參數用于設定較高的3 2位,而d w M a x i m u m S i z e L o w參數則用于設定較低的 3 2位值。對于 4 GB或小于4 GB的檔案來說,d w M a x i m u m S i z e H i g h的值将始終是0。

6 4位的值,意味着Wi n d o w s能夠處理最大為1 6 E B(1 0 1 8 位元組)的檔案。如果想要建立一個檔案映射對象,使它能夠反映檔案目前的大小,那麼可以為上面兩個參數傳遞 0。如果隻打算讀取該檔案或者通路檔案而不改變它的大小,那麼為這兩個參數傳遞 0。如果打算将資料附加給該檔案,可以選擇最大的檔案大小,以便為你留出一些富裕的空間。如果目前磁盤上的檔案包含0位元組,那麼可以給 C r e a t e F i l e M a p p i n g函數的d w M a x i m u m S i z e H i g h和d w M a x i m u mS i z e L o w傳遞兩個0。這樣做就可以告訴系統,你要的檔案映射對象裡面的存儲器為 0位元組。這是個錯誤,C r e a t e F i l e M a p p i n g将傳回N U L L。

Wi n d o w s支援最大為1 6 E B的檔案和檔案映射對象,這當然很好,但是,怎樣将這樣大的檔案映射到 3 2位程序的位址空間(3 2位位址空間是4 G B檔案的上限)中去呢?下一節介紹解決這個問題的辦法。當然,6 4位程序擁有16 EB的位址空間,是以可以進行更大的檔案的映射操作,但是,如果檔案是個超大規模的檔案,仍然會遇到類似的問題。

書中給了個例子,讓自己一步一步調看檔案大小變化:

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

上面是我寫的,CreadeFile會建立一個0位元組大小的aaaa.dat,然後下面那個CreateFileMapping會把檔案大小程式設計100位元組。裡面内容是空(NULL)。

C r e a t e F i l e M a p p i n g函數,傳遞PA G E _ R E A D W R I T E标志,那麼系統将設法確定磁盤上的相關資料檔案的大小至少與 d w M a x i m u m S i z e H i g h和d w M a x i m u m S i z e L o w參數中設定的大小相同。如果該檔案小于設定的大小, C r e a t e F i l e M a p p i n g函數将擴充該檔案的大小,使磁盤上的檔案變大。這種擴充是必要的,這樣,當以後将該檔案作為記憶體映射檔案使用時,實體存儲器就已經存在了。如果正在用 PA G E _ R E A D O N LY或PA G E _ W R I T E C O P Y标志建立該檔案映射對象,那麼C r e a t e F i l e M a p p i n g特定的檔案大小不得大于磁盤檔案的實體大小。這是因為你無法将任何資料附加給該檔案。

函數的最後一個參數是p s z N a m e。它是個以0結尾的字元串,用于給該檔案映射對象賦予一個名字。該名字用于與其他程序共享檔案映射對象(本章後面展示了它的一個例子。第3章詳細介紹了核心對象的共享操作)。記憶體映射資料檔案通常并不需要被共享,是以這個參數通常是N U L L。

系統建立檔案映射對象,并将用于辨別該對象的句柄傳回該調用線程。如果系統無法建立檔案映射對象,便傳回一個N U L L句柄值。記住,當C r e a t e F i l e運作失敗時,它将傳回I N VA L I D _H A N D L E _ VA L U E(定義為-1),當C r e a t e F i l e M a p p i n g運作失敗時,它傳回N U L L。請不要混淆這些錯誤值。

17.3.3 步驟3:将檔案資料映射到程序的位址空間

M a p Vi e w O f F i l e函數來進行這項操作:

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

h F i l e M a p p i n g O b j e c t用于辨別檔案映射對象的句柄,該句柄是前面調用CreateFile Mapping或O p e n F i l e M a p p i n g(本章後面介紹)函數傳回的。參數d w D e s i r e d A c c e s s用于辨別如何通路該資料。不錯,必須再次設定如何通路檔案的資料。可以設定表1 7 - 6所列的4個值中的一個。

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

3個參數與保留位址空間區域及将實體存儲器映射到該區域有關。當你将一個檔案映射到你的程序的位址空間中時,你不必一次性地映射整個檔案。相反,可以隻将檔案的一小部分映射到位址空間。被映射到程序的位址空間的這部分檔案稱為一個視圖,這可以說明M a p Vi e w O f F i l e是如何而得名的。

d w F i l e O ff s e t H i g h和d w F i l e O ff s e t L o w參數來進行這項操作。由于 Wi n d o w s支援的檔案最大可達1 6 E B,是以必須用一個 6 4位的值來設定這個位元組的位移值。這個 6 4位值中,較高的 3 2位傳遞給參數d w F i l e O ff s e t H i g h,較低的3 2位傳遞給參數d w F i l e O ff s e t L o w。注意,檔案中的這個位移值必須是系統的配置設定粒度的倍數(迄今為止,Wi n d o w s的所有實作代碼的配置設定粒度均為64 KB)。第1 4章介紹了如何擷取某個系統的配置設定粒度。

,資料檔案有多少位元組要映射到位址空間。這與設定要保留多大的位址空間區域的情況是相同的。可以使用 d w N u m b e r O f B y t e s To M a p參數來設定這個值。如果設定的值是0,那麼系統将設法把從檔案中的指定位移開始到整個檔案的結尾的視圖映射到位址空間。

    如果在調用M a p Vi e w O f F i l e函數時設定了F I L E _ M A P _ C O P Y标志,系統就會從系統的頁檔案中送出實體存儲器。送出的位址空間數量由 d w N u m b e r O f B y t e s To M a p參數決定。隻要你不進行其他操作,隻是從檔案的映像視圖中讀取資料,那麼系統将決不會使用頁檔案中的這些送出的頁面。但是,如果程序中的任何線程将資料寫入檔案的映像視圖中的任何記憶體位址,那麼系統将從頁檔案中抓取已送出頁面中的一個頁面,将原始資料頁面拷貝到該頁交換檔案中,然後将該拷貝的頁面映射到你的程序的位址空間。從這時起,你的程序中的線程就要通路資料的本地拷貝,不能讀取或修改原始資料。

17.3.4 步驟4:從程序的位址空間中撤消檔案資料的映像

    當不再需要保留映射到你的程序位址空間區域中的檔案資料時,可以通過調用下面的函數将它釋放:

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

17.3.5 步驟5和步驟6:關閉檔案映射對象和檔案對象

C l o s e H a n d l e函數,每個句柄調用一次:

Windows核心程式設計 第十七章 -記憶體映射檔案(下)

17.8 使用記憶體映射檔案在程序之間共享資料

總是出色地提供各種機制,使應用程式能夠迅速而友善地共享資料和資訊。這些機制包括R P C、C O M、O L E、D D E、視窗消息(尤其是 W M _ C O P Y D ATA)、剪貼闆、郵箱、管道和套接字等。在Wi n d o w s中,在單個計算機上共享資料的最低層機制是記憶體映射檔案。不錯,如果互相進行通信的所有程序都在同一台計算機上的話,上面提到的所有機制均使用記憶體映射檔案從事它們的煩瑣工作。如果要求達到較高的性能和較小的開銷,記憶體映射檔案是舉手可得的最佳機制。

    資料共享方法是通過讓兩個或多個程序映射同一個檔案映射對象的視圖來實作的,這意味着它們将共享實體存儲器的同一個頁面。是以,當一個程序将資料寫入一個共享檔案映射對象的視圖時,其他程序可以立即看到它們視圖中的資料變更情況。注意,如果多個程序共享單個檔案映射對象,那麼所有程序必須使用相同的名字來表示該檔案映射對象。

C r e a t e F i l e函數,打開磁盤上的. e x e檔案。然後系統調用C r e a t e F i l e M a p p i n g函數,建立一個檔案映射對象。最後,系統代表新建立的程序調用 M a p Vi e w O f F i l e E x函數(它帶有 S E C _ I M A G E标志),這樣, . e x e檔案就可以映射到程序的位址空間。這裡調用的是 M a p Vi e w O f F i l e E x,而不是M a p Vi e w O f F i l e,這樣,檔案的映像将被映射到存放在 . e x e檔案映像中的基位址中。系統建立該程序的主線程,将該映射視圖的可執行代碼的第一個位元組的位址放入線程的指令指針,然後C P U啟動該代碼的運作。

. e x e檔案已經存在一個檔案映射對象,是以不會建立新的檔案對象或者檔案映射對象。相反,系統将第二次映射該檔案的一個視圖,這次是在新建立的程序的位址空間環境中映射的。系統所做的工作是将相同的檔案同時映射到兩個位址空間。顯然,這是對記憶體的更有效的使用,因為兩個程序将共享包含正在執行的這部分代碼的實體存儲器的同一個頁面。與所有核心對象一樣,可以使用 3種方法與多個程序共享對象,這 3種方法是句柄繼承性、句柄命名和句柄複制。關于這3種方法的詳細說明,參見第3章的内容。

17.9 頁檔案支援的記憶體映射檔案

    到現在為止,已經介紹了映射駐留在磁盤驅動器上的檔案視圖的方法。許多應用程式在運作時都要建立一些資料,并且需要将資料傳送給其他程序,或者與其他程序共享。如果應用程式必須在磁盤驅動器上建立資料檔案,并且将資料存儲在磁盤上以便對它進行共享,那麼這将是非常不友善的。

M i c r o s o f t公司認識到了這一點,并且增加了一些功能,以便建立由系統的頁檔案支援的記憶體映射檔案,而不是由專用硬碟檔案支援的記憶體映射檔案。這個方法與建立記憶體映射磁盤檔案所用的方法幾乎相同,不同之處是它更加友善。一方面,它不必調用 C r e a t e F i l e函數,因為你不是要建立或打開一個指定的檔案,你隻需要像通常那樣調用 C r e a t e F i l e M a p p i n g函數,并且傳遞I N VA L I D _ H A N D L E _ VA L U E作為h F i l e參數。這将告訴系統,你不是建立其實體存儲器駐留在磁盤上的檔案中的檔案映射對象,相反,你想讓系統從它的頁檔案中送出實體存儲器。配置設定的存儲器的數量由C r e a t e F i l e M a p p i n g函數的d w M a x i m u m S i z e H i g h和d w M a x i m u m S i z e L o w兩個參數來決定。

C r e a t e F i l e M a p p i n g函數,并傳遞一個以0結尾的字元串作為p s z N a m e參數。然後,想要通路該存儲器的其他程序就可以調用C r e a t e F i l e M a p p i n g或O p e n F i l e M a p p i n g函數,并傳遞相同的名字。