MapViewOfFile

問題&原因

1览徒、問題

最近發(fā)現(xiàn)游戲中MapViewOfFile的時候會報錯:GetLastError() == ERROR_ACCESS_DENIED砾淌、ERROR_NOT_ENOUGH_MEMORY郑现、ERROR_OUTOFMOMORY.

代碼中的處理是釋放所有的映射空間缕坎,再重新映射琅催。但是再次映射的時候仍然會繼續(xù)報錯傅联,導(dǎo)致釋放映射空間的操作很費cpu,導(dǎo)致游戲卡頓厲害浩蓉。

2派继、原因

CreateFileMapping中指定的是Mapping的大小宾袜,在使用MapViewOfFile時捻艳,位置和大小之和不能超過Mapping,Mapping的大小是不能調(diào)整的庆猫,如要改變只能關(guān)閉后重新創(chuàng)建认轨;而View的大小除了不能超出Mapping之外,還會受到進程的虛擬地址空間的限制月培,View要使用連續(xù)的虛擬地址空間嘁字,由于程序運行過程中會有很多分配和釋放內(nèi)存的操作,導(dǎo)致地址空間形成很多“碎片”杉畜,在程序運行一端時間后纪蜒,可能虛擬內(nèi)存剩余的總量較大,但最大的連續(xù)地址空間卻不大此叠,在MapViewOfFile時如果找不到足夠的連續(xù)空間則會失敗纯续,所以MapViewOfFile最好在程序初始化時一次分配足夠的大小。


學(xué)習(xí)


CreateFileMapping 函數(shù)

HANDLE CreateFileMapping(

? HANDLE hFile,?????????????????????? //物理文件句柄

? LPSECURITY_ATTRIBUTES lpAttributes, //安全設(shè)置

? DWORD flProtect,??????????????????? //保護設(shè)置

? DWORD dwMaximumSizeHigh,??????????? //高位文件大小(因為我們的游戲是32位的灭袁,所以此處設(shè)為 0)

? DWORD dwMaximumSizeLow,???????????? //低位文件大小

? LPCTSTR lpName????????????????????? //共享內(nèi)存名稱

);

文件映射問題?

內(nèi)存映射文件并不是簡單的文件I/O操作猬错,實際用到了Windows的核心編程技術(shù)--內(nèi)存管理。所以茸歧,如果想對內(nèi)存映射文件有更深刻的認(rèn)識倦炒,必須對Windows操作系統(tǒng)的內(nèi)存管理機制有清楚的認(rèn)識,內(nèi)存管理的相關(guān)知識非常復(fù)雜软瞎,超出了本文的討論范疇逢唤,在此就不再贅述拉讯,感興趣的讀者可以參閱其他相關(guān)書籍。下面給出使用內(nèi)存映射文件的一般方法:?

?   首先要通過CreateFile()函數(shù)來創(chuàng)建或打開一個文件內(nèi)核對象智玻,這個對象標(biāo)識了磁盤上將要用作內(nèi)存映射文件的文件遂唧。在用CreateFile()將文件映像在物理存儲器的位置通告給操作系統(tǒng)后,只指定了映像文件的路徑吊奢,映像的長度還沒有指定盖彭。為了指定文件映射對象需要多大的物理存儲空間還需要通過CreateFileMapping()函數(shù)來創(chuàng)建一個文件映射內(nèi)核對象以告訴系統(tǒng)文件的尺寸以及訪問文件的方式。在創(chuàng)建了文件映射對象后页滚,還必須為文件數(shù)據(jù)保留一個地址空間區(qū)域召边,并把文件數(shù)據(jù)作為映射到該區(qū)域的物理存儲器進行提交。由MapViewOfFile()函數(shù)負(fù)責(zé)通過系統(tǒng)的管理而將文件映射對象的全部或部分映射到進程地址空間裹驰。此時隧熙,對內(nèi)存映射文件的使用和處理同通常加載到內(nèi)存中的文件數(shù)據(jù)的處理方式基本一樣,在完成了對內(nèi)存映射文件的使用時幻林,還要通過一系列的操作完成對其的清除和使用過資源的釋放贞盯。這部分相對比較簡單,可以通過UnmapViewOfFile()完成從進程的地址空間撤消文件數(shù)據(jù)的映像沪饺、通過CloseHandle()關(guān)閉前面創(chuàng)建的文件映射對象和文件對象躏敢。

內(nèi)存映射文件相關(guān)函數(shù)?

在使用內(nèi)存映射文件時,所使用的API函數(shù)主要就是前面提到過的那幾個函數(shù)整葡,下面分別對其進行介紹:?

HANDLE?? CreateFile(LPCTSTR?? lpFileName,?

DWORD?? dwDesiredAccess,?

DWORD?? dwShareMode,?

LPSECURITY_ATTRIBUTES?? lpSecurityAttributes,?

DWORD?? dwCreationDisposition,?

DWORD?? dwFlagsAndAttributes,?

HANDLE?? hTemplateFile);?

?   函數(shù)CreateFile()即使是在普通的文件操作時也經(jīng)常用來創(chuàng)建件余、打開文件,在處理內(nèi)存映射文件時遭居,該函數(shù)來創(chuàng)建/打開一個文件內(nèi)核對象啼器,并將其句柄返回,在調(diào)用該函數(shù)時需要根據(jù)是否需要數(shù)據(jù)讀寫和文件的共享方式來設(shè)置參數(shù)dwDesiredAccess和dwShareMode俱萍,錯誤的參數(shù)設(shè)置將會導(dǎo)致相應(yīng)操作時的失敗端壳。?

?HANDLE?? CreateFileMapping(HANDLE?? hFile,?

LPSECURITY_ATTRIBUTES?? lpFileMappingAttributes,?

DWORD?? flProtect,?

DWORD?? dwMaximumSizeHigh,?

DWORD?? dwMaximumSizeLow,?

LPCTSTR?? lpName);?

?   CreateFileMapping()函數(shù)創(chuàng)建一個文件映射內(nèi)核對象,通過參數(shù)hFile指定待映射到進程地址空間的文件句柄(該句柄由CreateFile()函數(shù)的返回值獲惹鼓ⅰ)损谦。由于內(nèi)存映射文件的物理存儲器實際是存儲于磁盤上的一個文件,而不是從系統(tǒng)的頁文件中分配的內(nèi)存腥寇,所以系統(tǒng)不會主動為其保留地址空間區(qū)域成翩,也不會自動將文件的存儲空間映射到該區(qū)域,為了讓系統(tǒng)能夠確定對頁面采取何種保護屬性赦役,需要通過參數(shù)flProtect來設(shè)定麻敌,保護屬性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分別表示文件映射對象被映射后掂摔,可以讀取术羔、讀寫文件數(shù)據(jù)赢赊。在使用PAGE_READONLY時,必須確保CreateFile()采用的是GENERIC_READ參數(shù)级历;PAGE_READWRITE則要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE參數(shù)释移;至于屬性PAGE_WRITECOPY則只需要確保CreateFile()采用了GENERIC_READ和GENERIC_WRITE其中之一即可。DWORD型的參數(shù)dwMaximumSizeHigh和dwMaximumSizeLow也是相當(dāng)重要的寥殖,指定了文件的最大字節(jié)數(shù)玩讳,由于這兩個參數(shù)共64位,因此所支持的最大文件長度為16EB嚼贡,幾乎可以滿足任何大數(shù)據(jù)量文件處理場合的要求熏纯。dwMaximumSizeHigh,dwMaximumSizeLow:如果這兩個參數(shù)為0粤策,則文件映射對象的最大長度等于hFile指定的文件長度樟澜。

LPVOID?? MapViewOfFile(HANDLE?? hFileMappingObject,?

DWORD?? dwDesiredAccess,?

DWORD?? dwFileOffsetHigh,?

DWORD?? dwFileOffsetLow,?

DWORD?? dwNumberOfBytesToMap);?

MapViewOfFile()函數(shù)負(fù)責(zé)把文件數(shù)據(jù)映射到進程的地址空間,參數(shù)hFileMappingObject為CreateFileMapping()返回的文件映像對象句柄叮盘。參數(shù)dwDesiredAccess則再次指定了對文件數(shù)據(jù)的訪問方式秩贰,而且同樣要與CreateFileMapping()函數(shù)所設(shè)置的保護屬性相匹配。雖然這里一再對保護屬性進行重復(fù)設(shè)置看似多余柔吼,但卻可以使應(yīng)用程序能更多的對數(shù)據(jù)的保護屬性實行有效控制毒费。MapViewOfFile()函數(shù)允許全部或部分映射文件,在映射時嚷堡,需要指定數(shù)據(jù)文件的偏移地址以及待映射的長度蝗罗。其中艇棕,文件的偏移地址由DWORD型的參數(shù)dwFileOffsetHigh和dwFileOffsetLow組成的64位值來指定蝌戒,而且必須是操作系統(tǒng)的分配粒度的整數(shù)倍,對于Windows操作系統(tǒng),分配粒度固定為64KB。當(dāng)然闷袒,也可以通過如下代碼來動態(tài)獲取當(dāng)前操作系統(tǒng)的分配粒度:?

SYSTEM_INFO?? sinf;?

GetSystemInfo(&sinf);?

DWORD?? dwAllocationGranularity?? =?? sinf.dwAllocationGranularity;?

參數(shù)dwNumberOfBytesToMap指定了數(shù)據(jù)文件的映射長度携添,這里需要特別指出的是,對于Windows?? 9x操作系統(tǒng)忧设,如果MapViewOfFile()無法找到足夠大的區(qū)域來存放整個文件映射對象,將返回空值(NULL);但是在Windows?? 2000下彩扔,MapViewOfFile()只需要為必要的視圖找到足夠大的一個區(qū)域即可,而無須考慮整個文件映射對象的大小僻爽。?

在完成對映射到進程地址空間區(qū)域的文件處理后虫碉,需要通過函數(shù)UnmapViewOfFile()完成對文件數(shù)據(jù)映像的釋放,該函數(shù)原型聲明如下:?

BOOL?? UnmapViewOfFile(LPCVOID?? lpBaseAddress);?

唯一的參數(shù)lpBaseAddress指定了返回區(qū)域的基地址胸梆,必須將其設(shè)定為MapViewOfFile()的返回值敦捧。在使用了函數(shù)MapViewOfFile()之后须板,必須要有對應(yīng)的UnmapViewOfFile()調(diào)用,否則在進程終止之前兢卵,保留的區(qū)域?qū)o法釋放习瑰。除此之外,前面還曾由CreateFile()和CreateFileMapping()函數(shù)創(chuàng)建過文件內(nèi)核對象和文件映射內(nèi)核對象秽荤,在進程終止之前有必要通過CloseHandle()將其釋放甜奄,否則將會出現(xiàn)資源泄漏的問題。?


解決方案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窃款,一起剝皮案震驚了整個濱河市贺嫂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雁乡,老刑警劉巖第喳,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異踱稍,居然都是意外死亡曲饱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門珠月,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扩淀,“玉大人,你說我怎么就攤上這事啤挎∽ぷ唬” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵庆聘,是天一觀的道長胜臊。 經(jīng)常有香客問我,道長伙判,這世上最難降的妖魔是什么象对? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮宴抚,結(jié)果婚禮上勒魔,老公的妹妹穿的比我還像新娘。我一直安慰自己菇曲,他們只是感情好冠绢,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著常潮,像睡著了一般弟胀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天邮利,我揣著相機與錄音弥雹,去河邊找鬼。 笑死延届,一個胖子當(dāng)著我的面吹牛剪勿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播方庭,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼厕吉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了械念?” 一聲冷哼從身側(cè)響起头朱,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎龄减,沒想到半個月后项钮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡希停,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年烁巫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宠能。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡亚隙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出违崇,到底是詐尸還是另有隱情阿弃,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布羞延,位于F島的核電站渣淳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肴楷。R本人自食惡果不足惜水由,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一荠呐、第九天 我趴在偏房一處隱蔽的房頂上張望赛蔫。 院中可真熱鬧,春花似錦泥张、人聲如沸呵恢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渗钉。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳄橘,已是汗流浹背声离。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瘫怜,地道東北人术徊。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像鲸湃,于是被迫代替她去往敵國和親赠涮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內(nèi)容