日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

發(fā)布時(shí)間:2025/3/15 c/c++ 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文背景:

在編程中,很多Windows或C++的內(nèi)存函數(shù)不知道有什么區(qū)別,更別談有效使用;根本的原因是,沒有清楚的理解操作系統(tǒng)的內(nèi)存管理機(jī)制,本文企圖通過簡單的總結(jié)描述,結(jié)合實(shí)例來闡明這個(gè)機(jī)制。

本文目的:

對Windows內(nèi)存管理機(jī)制了解清楚,有效的利用C++內(nèi)存函數(shù)管理和使用內(nèi)存。

本文內(nèi)容:

本文一共有六節(jié),由于篇幅較多,故按節(jié)發(fā)表。其他章節(jié)請看本人博客的Windows內(nèi)存管理及C++內(nèi)存分配實(shí)例(一)(二)(三)(五)和(六)。

4.??????內(nèi)存管理機(jī)制--內(nèi)存映射文件?(Map)

??? 和虛擬內(nèi)存一樣,內(nèi)存映射文件可以用來保留一個(gè)進(jìn)程地址區(qū)域;但是,與虛擬內(nèi)存不同,它提交的不是物理內(nèi)存或是虛擬頁文件,而是硬盤上的文件。

·????????使用場合

它有三個(gè)主要用途:

系統(tǒng)加載EXE和DLL文件

操作系統(tǒng)就是用它來加載exe和dll文件建立進(jìn)程,運(yùn)行exe。這樣可以節(jié)省頁文件和啟動時(shí)間。

訪問大數(shù)據(jù)文件

如果文件太大,比如超過了進(jìn)程用戶區(qū)2G,用fopen是不能對文件進(jìn)行操作的。這時(shí),可用內(nèi)存映射文件。對于大數(shù)據(jù)文件可以不必對文件執(zhí)行I/O操作,不必對所有文件內(nèi)容進(jìn)行緩存。

進(jìn)程共享機(jī)制

內(nèi)存映射文件是多個(gè)進(jìn)程共享數(shù)據(jù)的一種較高性能的有效方式,它也是操作系統(tǒng)進(jìn)程通信機(jī)制的底層實(shí)現(xiàn)方法。RPC、COM、OLE、DDE、窗口消息、剪貼板、管道、Socket等都是使用內(nèi)存映射文件實(shí)現(xiàn)的。

·????????系統(tǒng)加載EXE和DLL文件

ü??????EXE文件格式

每個(gè)EXE和DLL文件由許多節(jié)(Section)組成,每個(gè)節(jié)都有保護(hù)屬性:READ,WRITE,EXECUTE和SHARED(可以被多個(gè)進(jìn)程共享,關(guān)閉頁面的COPY-ON-WRITE屬性)。

以下是常見的節(jié)和作用:

節(jié)名

作用

.text

.exe和.dll文件的代碼

.data

已經(jīng)初始化的數(shù)據(jù)

.bss

未初始化的數(shù)據(jù)

.reloc

重定位表(裝載進(jìn)程的進(jìn)程地址空間)

.rdata

運(yùn)行期只讀數(shù)據(jù)

.CRT

C運(yùn)行期只讀數(shù)據(jù)

.debug

調(diào)試信息

.xdata

異常處理表

.tls

線程的本地化存儲

.idata

輸入文件名表

.edata

輸出文件名表

.rsrc

資源表

.didata

延遲輸入文件名表

?

ü??????加載過程

1.??????系統(tǒng)根據(jù)exe文件名建立進(jìn)程內(nèi)核對象、頁目和頁表,也就是建立了進(jìn)程的虛擬空間。

2.??????讀取exe文件的大小,在默認(rèn)基地址0x0040 0000上保留適當(dāng)大小的區(qū)域。可以在鏈接程序時(shí)用/BASE?選項(xiàng)更改基地址(在VC工程屬性/鏈接器/高級上設(shè)置)。提交時(shí),操作系統(tǒng)會管理頁目和頁表,將硬盤上的文件映射到進(jìn)程空間中,頁表中保存的地址是exe文件的頁偏移。

3.??????讀取exe文件的.idata節(jié),此節(jié)列出exe所用到的所有dll文件。然后和

exe文件一樣,將dll文件映射到進(jìn)程空間中。如果無法映射到基地址,系統(tǒng)會重新定位。

4.???映射成功后,系統(tǒng)會把第一頁代碼加載到內(nèi)存,然后更新頁目和頁

表。將第一條指令的地址交給線程指令指針。當(dāng)系統(tǒng)執(zhí)行時(shí),發(fā)現(xiàn)代碼沒有在內(nèi)存中,會將exe文件中的代碼加載到內(nèi)存中。

??????????????

ü??????第二次加載時(shí)(運(yùn)行多個(gè)進(jìn)程實(shí)例)

1.??????建立進(jìn)程、映射進(jìn)程空間都跟前面一樣,只是當(dāng)系統(tǒng)發(fā)現(xiàn)這個(gè)exe已

??????經(jīng)建立了內(nèi)存映射文件對象時(shí),它就直接映射到進(jìn)程空間了;只是當(dāng)

?????系統(tǒng)分配物理頁面時(shí),根據(jù)節(jié)的保護(hù)屬性賦予頁面保護(hù)屬性,對于代碼

?????節(jié)賦予READ屬性,全局變量節(jié)賦予COPY-ON-WRITE屬性。

2.??????不同的實(shí)例共享代碼節(jié)和其他的節(jié),當(dāng)實(shí)例需要改變頁面內(nèi)容時(shí),會

??????拷貝頁面內(nèi)容到新頁面,更新頁目和頁表。

3.??????對于不同進(jìn)程實(shí)例需要共享的變量,exe文件有一

??????個(gè)默認(rèn)的節(jié),?給這個(gè)節(jié)賦予SHARED屬性。

4.??????你也可以創(chuàng)建自己的SHARED節(jié)

#pragma data_seg(“節(jié)名”)

Long instCount;

#pragma data_seg()

然后,你需要在鏈接程序時(shí)告訴編譯器節(jié)的默認(rèn)屬性。

/SECTION:?節(jié)名,RWS

或者,在程序里用以下表達(dá)式:

#pragma comment(linker,“/SECTION:節(jié)名,RWS”)

這樣的話編譯器會創(chuàng)建.drective節(jié)來保存上述命令,然后鏈接時(shí)會用它改變節(jié)屬性。

注意,共享變量有可能有安全隱患,因?yàn)樗梢宰x到其他進(jìn)程的數(shù)據(jù)。

C++程序:多個(gè)進(jìn)程共享變量舉例

*.cpp開始處:

#pragma?data_seg(".share")

long?shareCount=0;

#pragma?data_seg()

#pragma?comment(linker,"/SECTION:.share,RWS")

ShareCount++;

?

注意,同一個(gè)exe文件產(chǎn)生的進(jìn)程會共享shareCount,必須是處于同一個(gè)位置上exe

?

·????????訪問大數(shù)據(jù)文件

ü??????創(chuàng)建文件內(nèi)核對象

使用CreateFile(文件名,訪問屬性,共享模式,…)?API可以創(chuàng)建。

其中,訪問屬性有:

0?不能讀寫?(用它可以訪問文件屬性)

GENERIC_READ

GENERIC_WRITE

GENERIC_READ|GENERIC_WRITE;

共享模式:

0?獨(dú)享文件,其他應(yīng)用程序無法打開

FILE_SHARE_WRITE

FILE_SHARE_READ|FILE_SHARE_WRITE

這個(gè)屬性依賴于訪問屬性,必須和訪問屬性不沖突。

當(dāng)創(chuàng)建失敗時(shí),返回INVALID_HANDLE_VALUE。

?

C++程序如下:

試圖打開一個(gè)1G的文件:

MEMORYSTATUS memStatus;

GlobalMemoryStatus(&memStatus);

HANDLE hn=CreateFile(L"D://1G.rmvb",GENERIC_READ|GENERIC_WRITE,

FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

??????????????if(hn==INVALID_HANDLE_VALUE)

????????????????????????cout<<"打開文件失敗!"<<endl;

??????????????FILE *p=fopen("D://1G.rmvb","rb");

??????????????if(p==NULL)

????????????????????????cout<<"用fopen不能打開大文件!"<<endl;

??????????????MEMORYSTATUS memStatus2;

??????????????GlobalMemoryStatus(&memStatus2);

??????????????cout<<"打開文件后的空間:"<<endl;

cout<<"減少物理內(nèi)存="<<memStatus.dwAvailPhys-memStatus2.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus.dwAvailPageFile-memStatus2.dwAvailPageFile<<endl;

cout<<"減少可用進(jìn)程空間="

<<memStatus.dwAvailVirtual-memStatus2.dwAvailVirtual<<endl<<endl;

結(jié)果如下:

?

可見,系統(tǒng)需要一些內(nèi)存來管理內(nèi)核對象,每一次運(yùn)行的結(jié)果都不一樣,但差別不會太大。

用c語言的fopen不能打開這么大的文件。理論上,32位系統(tǒng)能支持232字節(jié),但是,進(jìn)程空間只有2G,它只能表示那么大的空間。

ü??????創(chuàng)建文件映射內(nèi)核對象

API如下:

HANDLE CreateFileMapping(Handle?文件,PSECURITY_ATTRIBUTES?安全屬性,DWORD?保護(hù)屬性,DWORD?文件大小高32位,DWORD?文件大小低32位,PCTSTR??映射名稱)

“文件”是上面創(chuàng)建的句柄;

“安全屬性”是內(nèi)核對象需要的,NULL表示使用系統(tǒng)默認(rèn)的安全屬性;“保護(hù)屬性”是當(dāng)將存儲器提交給進(jìn)程空間時(shí),需要的頁面屬性:PAGE_READONLY, PAGE_READWRITE和PAGE_WRITECOPY。這個(gè)屬性不能和文件對象的訪問屬性沖突。除了這三個(gè)外,還有兩個(gè)屬性可以和它們連接使用(|)。當(dāng)更新文件內(nèi)容時(shí),不提供緩存,直接寫入文件,可用SEC_NOCACHE;當(dāng)文件是可執(zhí)行文件時(shí),系統(tǒng)會根據(jù)節(jié)賦予不同的頁面屬性,可用SEC_IMAGE。另外,SEC_RESERVE和SEC_COMMIT用于稀疏提交的文件映射,詳細(xì)介紹請參考下文。

“文件大小高32位”和“文件大小低32位”聯(lián)合起來告訴系統(tǒng),這個(gè)映射所能支持的文件大小(操作系統(tǒng)支持264B文件大小);當(dāng)這個(gè)值大于實(shí)際的文件大小時(shí),系統(tǒng)會擴(kuò)大文件到這個(gè)值,因?yàn)橄到y(tǒng)需要保證進(jìn)程空間能完全被映射。值為0默認(rèn)為文件的大小,這時(shí)候如果文件大小為0,創(chuàng)建失敗。

“映射名稱”是給用戶標(biāo)識此內(nèi)核對象,供各進(jìn)程共享,如果為NULL,則不能共享。

對象創(chuàng)建失敗時(shí)返回NULL。

創(chuàng)建成功后,系統(tǒng)仍未為文件保留進(jìn)程空間。

?

C++程序:

????????????????????????MEMORYSTATUS memStatus2;

????????????????????????GlobalMemoryStatus(&memStatus2);

HANDLE hmap=CreateFileMapping(hn,NULL,PAGE_READWRITE,0,0,L"Yeming-Map");

????????????????????????if(hmap==NULL)

????????????????????????cout<<"建立內(nèi)存映射對象失敗!"<<endl;

????????????????????????MEMORYSTATUS memStatus3;

????????????????????????GlobalMemoryStatus(&memStatus3);

????????????????????????cout<<"建立內(nèi)存映射文件后的空間:"<<endl;

cout<<"減少物理內(nèi)存="<<memStatus2.dwAvailPhys-memStatus3.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus2.dwAvailPageFile-memStatus3.dwAvailPageFile<<endl;

?????????cout<<"減少可用進(jìn)程空間="

<<memStatus2.dwAvailVirtual-memStatus3.dwAvailVirtual<<endl<<endl;

????????????結(jié)果如下:

??????

?

默認(rèn)內(nèi)存映射的大小是1G文件。沒有損失內(nèi)存和進(jìn)程空間。它所做的是建立內(nèi)核對象,收集一些屬性。

?

ü??????文件映射內(nèi)核對象映射到進(jìn)程空間

API如下:

PVOID MAPViewOfFile(HANDLE?映射對象,DWORD訪問屬性,DWORD?偏移量高32位,DWORD?偏移量低32位,SIZE_T?字節(jié)數(shù))

“映射對象”是前面建立的對象;

“訪問屬性”可以是下面的值:FILE_MAP_WRITE(讀和寫)、FILE_MAP_READ、FILE_MAP_ALL_ACCESS(讀和寫)、FILE_MAP_COPY。當(dāng)使用FILE_MAP_COPY時(shí),系統(tǒng)分配虛擬頁文件,當(dāng)有寫操作時(shí),系統(tǒng)會拷貝數(shù)據(jù)到這些頁面,并賦予PAGE_READWRITE屬性。

可以看到,每一步都需要設(shè)置這類屬性,是為了可以多點(diǎn)控制,試想,如果在這一步想有多種不同的屬性操作文件的不同部分,就比較有用。

“偏移高32位”和“偏移低32位”聯(lián)合起來標(biāo)識映射的開始字節(jié)(地址是分配粒度的倍數(shù));

“字節(jié)數(shù)”指映射的字節(jié)數(shù),默認(rèn)0為到文件尾。

?

當(dāng)你需要指定映射到哪里時(shí),你可以使用:

PVOID MAPViewOfFile(HANDLE?映射對象,DWORD訪問屬性,DWORD?偏移量高32位,DWORD?偏移量低32位,SIZE_T?字節(jié)數(shù),PVOID?基地址)

“基地址”是映射到進(jìn)程空間的首地址,必須是分配粒度的倍數(shù)。

?

C++程序:

MEMORYSTATUS memStatus3;

????????????GlobalMemoryStatus(&memStatus3);

????????????LPVOID pMAP=MapViewOfFile(hmap,FILE_MAP_WRITE,0,0,0);

????????????cout<<"映射內(nèi)存映射文件后的空間:"<<endl;

if(pMAP==NULL)

???????????????cout<<"映射進(jìn)程空間失敗!"<<endl;

????????????else

???????????????printf("首地址=%x/n",pMAP);

????????????MEMORYSTATUS memStatus4;

????????????GlobalMemoryStatus(&memStatus4);

cout<<"減少物理內(nèi)存="<<memStatus3.dwAvailPhys-memStatus4.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus3.dwAvailPageFile-memStatus4.dwAvailPageFile<<endl;

cout<<"減少可用進(jìn)程空間="

<<memStatus3.dwAvailVirtual-memStatus4.dwAvailVirtual<<endl<<endl;

結(jié)果如下:

?

進(jìn)程空間減少了1G,系統(tǒng)同時(shí)會開辟一些內(nèi)存來做文件緩存。

ü??????使用文件

1.??????對于大文件,可以用多次映射的方法達(dá)到訪問的目的。有點(diǎn)像AWE技術(shù)。

2.??????Windows只保證同一文件映射內(nèi)核對象的多次映射的數(shù)據(jù)一致性,比如,當(dāng)有兩次映射同一對象到二個(gè)進(jìn)程空間時(shí),一個(gè)進(jìn)程空間的數(shù)據(jù)改變后,另一個(gè)進(jìn)程空間的數(shù)據(jù)也會跟著改變;不保證不同映射內(nèi)核對象的多次映射的一致性。所以,使用文件映射時(shí),最好在CreateFile時(shí)將共享模型設(shè)置為0獨(dú)享,當(dāng)然,對于只讀文件沒這個(gè)必要。

????C++程序:使用1G的文件

MEMORYSTATUS memStatus4;

????????????????????????GlobalMemoryStatus(&memStatus4);

????????????????????????cout<<"讀取1G文件前:"<<endl;

????????????????????????cout<<"可用物理內(nèi)存="<<memStatus4.dwAvailPhys<<endl;

????????????????????????cout<<"可用頁文件="<<memStatus4.dwAvailPageFile<<endl;

????????????????????????cout<<"可用進(jìn)程空間="<<memStatus4.dwAvailVirtual<<endl<<endl;

????????????????????????int* pInt=(int*)pMAP;

????????????????????????cout<<"更改前="<<pInt[1000001536/4-1]<<endl;//文件的最后一個(gè)整數(shù)

????????????????????????for(int?i=0;i<1000001536/4-1;i++)

?????????????????????????????pInt[i]++;

????????????????????????pInt[1000001536/4-1]=10;

????????????????????????pInt[100]=90;

????????????????????????pInt[101]=100;

????????????????????????cout<<"讀取1G文件后:"<<endl;

????????????????????????MEMORYSTATUS memStatus5;

????????????????????????GlobalMemoryStatus(&memStatus5);

????????????????????????cout<<"可用物理內(nèi)存="<<memStatus5.dwAvailPhys<<endl;

????????????????????????cout<<"可用頁文件="<<memStatus5.dwAvailPageFile<<endl;

????????????????????????cout<<"可用進(jìn)程空間="<<memStatus5.dwAvailVirtual<<endl<<endl;

???????????

結(jié)果如下:

?

程序?qū)?G文件的各個(gè)整型數(shù)據(jù)加1,從上圖看出內(nèi)存損失了600多兆,但有時(shí)候損失不過十幾兆,可能跟系統(tǒng)當(dāng)時(shí)的狀態(tài)有關(guān)。

不管怎樣,這樣你完全看不到I/O操作,就像訪問普通數(shù)據(jù)結(jié)構(gòu)一樣方便。

?

ü??????保存文件修改

為了提高速度,更改文件時(shí)可能只更改到了系統(tǒng)緩存,這時(shí),需要強(qiáng)制保存更改到硬盤,特別是撤銷映射前。

BOOL FlushViewOfFile(PVOID?進(jìn)程空間地址,SIZE_T?字節(jié)數(shù))

“進(jìn)程空間地址”指的是需要更改的第一個(gè)字節(jié)地址,系統(tǒng)會變成頁面的地址;

“字節(jié)數(shù)”,系統(tǒng)會變成頁面大小的倍數(shù)。

寫入磁盤后,函數(shù)返回,對于網(wǎng)絡(luò)硬盤,如果希望寫入網(wǎng)絡(luò)硬盤后才返回的話,需要將FILE_FLAG_WRITE_THROUGH參數(shù)傳給CreateFile。

?

當(dāng)使用FILE_MAP_COPY建立映射時(shí),由于對數(shù)據(jù)的更改只是對虛擬頁文件的修改而不是硬盤文件的修改,當(dāng)撤銷映射時(shí),會丟失所做的修改。如果要保存,怎么辦?

你可以用FILE_MAP_WRITE建立另外一個(gè)映射,它映射到進(jìn)程的另外一段空間;掃描第一個(gè)映射的PAGE_READWRITE頁面(因?yàn)閷傩员桓?,如果頁面改變,用MoveMemory或其他拷貝函數(shù)將頁面內(nèi)容拷貝到第二次映射的空間里,然后再調(diào)用FlushViewOfFile。當(dāng)然,你要記錄哪個(gè)頁面被更改。

ü??????撤銷映射

用以下API可以撤銷映射:

BOOL??UnmapViewOfFile(PVOID pvBaseAddress)

這個(gè)地址必須與MapViewOfFile返回值相同。

?

ü??????關(guān)閉內(nèi)核對象

在不需要內(nèi)核對象時(shí),盡早將其釋放,防止內(nèi)存泄露。由于它們是內(nèi)核對象,調(diào)用CloseHandle(HANDLE)就可以了。

在CreateFileMapping后馬上關(guān)閉文件句柄;

在MapViewOfFile后馬上關(guān)閉內(nèi)存映射句柄;

最后再撤銷映射。

·????????進(jìn)程共享機(jī)制

ü??????基于硬盤文件的內(nèi)存映射

如果進(jìn)程需要共享文件,只要按照前面的方式建立內(nèi)存映射對象,然后按照名字來共享,那么進(jìn)程就可以映射這個(gè)對象到自己的進(jìn)程空間中。

C++程序如下:

HANDLE mapYeming=OpenFileMapping(FILE_MAP_WRITE,true,L"Yeming-Map");

????????????????????????if(mapYeming==NULL)

????????????????????????cout<<"找不到內(nèi)存映射對象:Yeming-Map!"<<endl;

????????????????????????MEMORYSTATUS memStatus3;

????????????????????????GlobalMemoryStatus(&memStatus3);

LPVOID pMAP=MapViewOfFile(mapYeming,FILE_MAP_WRITE,0,0,100000000);

????????????????????????cout<<"建立內(nèi)存映射文件后的空間:"<<endl;

????????????????????????if(pMAP==NULL)

????????????????????????cout<<"映射進(jìn)程空間失敗!"<<endl;

????????????????????????else

????????????????????????printf("首地址=%x/n",pMAP);

???????????

????????????????????????MEMORYSTATUS memStatus4;

????????????????????????GlobalMemoryStatus(&memStatus4);

???????????

cout<<"減少物理內(nèi)存="<<memStatus3.dwAvailPhys-memStatus4.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus3.dwAvailPageFile-memStatus4.dwAvailPageFile<<endl;

cout<<"減少可用進(jìn)程空間="<<memStatus3.dwAvailVirtual-memStatus4.dwAvailVirtual<<endl<<endl;

?

????????????????????????int* pInt=(int*)pMAP;

?????????cout<<pInt[100]<<endl;

????????

?????????結(jié)果如下:

?

在2.exe中打開之前1.exe創(chuàng)建的內(nèi)存映射對象(當(dāng)然,1.exe得處于運(yùn)行狀態(tài)),然后映射進(jìn)自己的進(jìn)程空間,當(dāng)1.exe改變文件的值時(shí),2.exe的文件對應(yīng)值也跟著改變,Windows保證同一個(gè)內(nèi)存映射對象映射出來的數(shù)據(jù)是一致的。可以看見,1.exe將值從90改為91,2.exe也跟著改變,因?yàn)樗鼈冇泄餐木彌_頁。

?

ü??????基于頁文件的內(nèi)存映射

如果只想共享內(nèi)存數(shù)據(jù)時(shí),沒有必要創(chuàng)建硬盤文件,再建立映射。可以直

接建立映射對象:

只要傳給CreateFileMapping一個(gè)文件句柄INVALID_HANDLE_VALUE就行了。所以,CreateFile時(shí),一定要檢查返回值,否則會建立一個(gè)基于頁文件的內(nèi)存映射對象。接下來就是映射到進(jìn)程空間了,這時(shí),系統(tǒng)會分配頁文件給它。

C++程序如下:

?

HANDLE hPageMap=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,

100000000,L"Yeming-Map-Page");

????????????if(hPageMap==NULL)

????????????????????????cout<<"建立基于頁文件的內(nèi)存映射對象失敗!"<<endl;

????????????MEMORYSTATUS memStatus6;

????????????GlobalMemoryStatus(&memStatus6);

????????????cout<<"建立基于頁文件的內(nèi)存映射文件后的空間:"<<endl;

cout<<"減少物理內(nèi)存="<<memStatus5.dwAvailPhys-memStatus6.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus5.dwAvailPageFile-memStatus6.dwAvailPageFile<<endl;

cout<<"減少可用進(jìn)程空間="<<memStatus5.dwAvailVirtual-memStatus6.dwAvailVirtual<<endl<<endl;???????????

LPVOID pPageMAP=MapViewOfFile(hPageMap,FILE_MAP_WRITE,0,0,0);????????

????????????結(jié)果如下:

???????

?

可見,和基于數(shù)據(jù)文件的內(nèi)存映射不同,現(xiàn)在剛建立內(nèi)核對象時(shí)就分配了所要的100M內(nèi)存。好處是,別的進(jìn)程可以通過這個(gè)內(nèi)核對象共享這段內(nèi)存,只要它也做了映射。

?

ü??????稀疏內(nèi)存映射文件

在虛擬內(nèi)存一節(jié)中,提到了電子表格程序。虛擬內(nèi)存解決了表示很少單元格有數(shù)據(jù)但必須分配所有內(nèi)存的內(nèi)存浪費(fèi)問題;但是,如果想在多個(gè)進(jìn)程之間共享這個(gè)電子表格結(jié)構(gòu)呢?

如果用基于頁文件的內(nèi)存映射,需要先分配頁文件,還是浪費(fèi)了空間,沒有了虛擬內(nèi)存的優(yōu)點(diǎn)。

Windows提供了稀疏提交的內(nèi)存映射機(jī)制。

當(dāng)使用CreateFileMapping時(shí),保護(hù)屬性用SEC_RESERVE時(shí),其不提交物理存儲器,使用SEC_COMMIT時(shí),其馬上提交物理存儲器。注意,只有文件句柄為INVALID_HANDLE_VALUE時(shí),才能使用這兩個(gè)參數(shù)。

按照通常的方法映射時(shí),系統(tǒng)只保留進(jìn)程地址空間,不會提交物理存儲器。

當(dāng)需要提交物理內(nèi)存時(shí)才提交,利用通常的VirtualAlloc函數(shù)就可以提交。

當(dāng)釋放內(nèi)存時(shí),不能調(diào)用VirtualFree函數(shù),只能調(diào)用UnmapViewOfFile來撤銷映射,從而釋放內(nèi)存。

?

C++程序如下:

HANDLE hVirtualMap=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE|SEC_RESERVE,0,100000000,L"Yeming-Map-Virtual");

if(hPageMap==NULL)

????????????????????????cout<<"建立基于頁文件的稀疏內(nèi)存映射對象失敗!"<<endl;

????????????MEMORYSTATUS memStatus8;

????????????GlobalMemoryStatus(&memStatus8);

????????????cout<<"建立基于頁文件的稀疏內(nèi)存映射文件后的空間:"<<endl;

cout<<"減少物理內(nèi)存="<<memStatus7.dwAvailPhys-memStatus8.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus7.dwAvailPageFile-memStatus8.dwAvailPageFile<<endl;

cout<<"減少可用進(jìn)程空間="<<memStatus7.dwAvailVirtual-memStatus8.dwAvailVirtual<<endl<<endl;

???????????

LPVOID pVirtualMAP=MapViewOfFile(hVirtualMap,FILE_MAP_WRITE,0,0,0);

????????????cout<<"內(nèi)存映射進(jìn)程后的空間:"<<endl;

????????????if(pVirtualMAP==NULL)

????????????????????????cout<<"映射進(jìn)程空間失敗!"<<endl;

????????????else

????????????????????????printf("首地址=%x/n",pVirtualMAP);

???????????

????????????MEMORYSTATUS memStatus9;

????????????GlobalMemoryStatus(&memStatus9);

???????????

cout<<"減少物理內(nèi)存="<<memStatus8.dwAvailPhys-memStatus9.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus8.dwAvailPageFile-memStatus9.dwAvailPageFile<<endl;

cout<<"減少可用進(jìn)程空間="<<memStatus8.dwAvailVirtual-memStatus9.dwAvailVirtual<<endl<<endl;

????????

結(jié)果如下:

?

用了SEC_RESERVE后,只是建立了一個(gè)內(nèi)存映射對象,和普通的一樣;不同的是,它映射完后,得到了一個(gè)虛擬進(jìn)程空間。現(xiàn)在,這個(gè)空間沒有分配任何的物理存儲器給它,你可以用VirtualAlloc?提交存儲器給它,詳細(xì)請參考上一篇<虛擬內(nèi)存(VM)>。

注意,你不可以用VirtualFree來釋放了,只能用UnmapViewOfFile來。

C++程序如下:

LPVOID pP=VirtualAlloc(pVirtualMAP,100*1000*1000,MEM_COMMIT,PAGE_READWRITE);?

????????????MEMORYSTATUS memStatus10;

????????????GlobalMemoryStatus(&memStatus10);

???????????

cout<<"減少物理內(nèi)存="<<memStatus9.dwAvailPhys-memStatus10.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus9.dwAvailPageFile-memStatus10.dwAvailPageFile<<endl;

cout<<"減少可用進(jìn)程空間="<<memStatus9.dwAvailVirtual-memStatus10.dwAvailVirtual<<endl<<endl;

?

????????????bool?result=VirtualFree(pP,100000000,MEM_DECOMMIT);

????????????if(!result)

????????????????????????cout<<"釋放失敗!"<<endl;

?????????????result=VirtualFree(pP,100000000,MEM_RELEASE);

????????????if(!result)

????????????????????????cout<<"釋放失敗!"<<endl;

?

????????????CloseHandle(hVirtualMap);

????????????MEMORYSTATUS memStatus11;

????????????GlobalMemoryStatus(&memStatus11);

cout<<"增加物理內(nèi)存="<<memStatus11.dwAvailPhys-memStatus10.dwAvailPhys<<endl;

cout<<"增加可用頁文件="<<memStatus11.dwAvailPageFile-memStatus10.dwAvailPageFile<<endl;

cout<<"增加可用進(jìn)程空間="<<memStatus11.dwAvailVirtual-memStatus10.dwAvailVirtual<<endl<<endl;

?

????????????result=UnmapViewOfFile(pVirtualMAP);

????????????if(!result)

????????????????????????cout<<"撤銷映射失敗!"<<endl;

?

????????????MEMORYSTATUS memStatus12;

????????????GlobalMemoryStatus(&memStatus12);

cout<<"增加物理內(nèi)存="<<memStatus12.dwAvailPhys-memStatus11.dwAvailPhys<<endl;

cout<<"增加可用頁文件="<<memStatus12.dwAvailPageFile-memStatus11.dwAvailPageFile<<endl;

cout<<"增加可用進(jìn)程空間="

<<memStatus12.dwAvailVirtual-memStatus11.dwAvailVirtual<<endl<<endl;

結(jié)果如下:

?

可以看見,用VirtualFree是不能夠釋放這個(gè)稀疏映射的;最后用UnmapViewOfFile得以釋放進(jìn)程空間和物理內(nèi)存。

?

總結(jié)

以上是生活随笔為你收集整理的全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。