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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

c/c++

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

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

本文背景:

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

本文目的:

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

本文內(nèi)容:

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

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

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

·????????使用場(chǎng)合

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

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

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

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

如果文件太大,比如超過(guò)了進(jìn)程用戶區(qū)2G,用fopen是不能對(duì)文件進(jìn)行操作的。這時(shí),可用內(nèi)存映射文件。對(duì)于大數(shù)據(jù)文件可以不必對(duì)文件執(zhí)行I/O操作,不必對(duì)所有文件內(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)閉頁(yè)面的COPY-ON-WRITE屬性)。

以下是常見(jiàn)的節(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

線程的本地化存儲(chǔ)

.idata

輸入文件名表

.edata

輸出文件名表

.rsrc

資源表

.didata

延遲輸入文件名表

?

ü??????加載過(guò)程

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

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

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

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

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

表。將第一條指令的地址交給線程指令指針。當(dāng)系統(tǒng)執(zhí)行時(shí),發(fā)現(xiàn)代碼沒(méi)有在內(nèi)存中,會(huì)將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)存映射文件對(duì)象時(shí),它就直接映射到進(jìn)程空間了;只是當(dāng)

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

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

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

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

3.??????對(duì)于不同進(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”)

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

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

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

*.cpp開(kāi)始處:

#pragma?data_seg(".share")

long?shareCount=0;

#pragma?data_seg()

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

ShareCount++;

?

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

?

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

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

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

其中,訪問(wèn)屬性有:

0?不能讀寫(xiě)?(用它可以訪問(wèn)文件屬性)

GENERIC_READ

GENERIC_WRITE

GENERIC_READ|GENERIC_WRITE;

共享模式:

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

FILE_SHARE_WRITE

FILE_SHARE_READ|FILE_SHARE_WRITE

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

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

?

C++程序如下:

試圖打開(kāi)一個(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<<"打開(kāi)文件失敗!"<<endl;

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

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

????????????????????????cout<<"用fopen不能打開(kāi)大文件!"<<endl;

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

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

??????????????cout<<"打開(kāi)文件后的空間:"<<endl;

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

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

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

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

結(jié)果如下:

?

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

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

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

API如下:

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

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

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

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

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

對(duì)象創(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)存映射對(duì)象失敗!"<<endl;

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

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

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

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

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

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

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

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

??????

?

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

?

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

API如下:

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

“映射對(duì)象”是前面建立的對(duì)象;

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

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

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

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

?

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

PVOID MAPViewOfFile(HANDLE?映射對(duì)象,DWORD訪問(wèn)屬性,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<<"減少可用頁(yè)文件="<<memStatus3.dwAvailPageFile-memStatus4.dwAvailPageFile<<endl;

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

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

結(jié)果如下:

?

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

ü??????使用文件

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

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

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

MEMORYSTATUS memStatus4;

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

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

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

????????????????????????cout<<"可用頁(yè)文件="<<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<<"可用頁(yè)文件="<<memStatus5.dwAvailPageFile<<endl;

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

???????????

結(jié)果如下:

?

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

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

?

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

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

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

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

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

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

?

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

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

ü??????撤銷(xiāo)映射

用以下API可以撤銷(xiāo)映射:

BOOL??UnmapViewOfFile(PVOID pvBaseAddress)

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

?

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

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

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

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

最后再撤銷(xiāo)映射。

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

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

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

C++程序如下:

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

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

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

?

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

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

接建立映射對(duì)象:

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

C++程序如下:

?

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

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

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

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

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

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

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

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

cout<<"減少可用頁(yè)文件="<<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é)果如下:

???????

?

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

?

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

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

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

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

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

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

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

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

?

C++程序如下:

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

if(hPageMap==NULL)

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

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

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

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

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

cout<<"減少可用頁(yè)文件="<<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<<"減少可用頁(yè)文件="<<memStatus8.dwAvailPageFile-memStatus9.dwAvailPageFile<<endl;

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

????????

結(jié)果如下:

?

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

注意,你不可以用VirtualFree來(lái)釋放了,只能用UnmapViewOfFile來(lái)。

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<<"減少可用頁(yè)文件="<<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<<"增加可用頁(yè)文件="<<memStatus11.dwAvailPageFile-memStatus10.dwAvailPageFile<<endl;

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

?

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

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

????????????????????????cout<<"撤銷(xiāo)映射失敗!"<<endl;

?

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

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

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

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

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

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

結(jié)果如下:

?

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

?

總結(jié)

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

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