江蘇 網(wǎng)站 備案黃頁引流推廣網(wǎng)站入口
內(nèi)存映射文件概念:
通過內(nèi)存映射文件函數(shù),可以將磁盤上的文件的全部或部分映射到進(jìn)程的虛擬地址空間的某個(gè)位置,一旦完成了映射,就可以通過指針像訪問內(nèi)存一樣訪問磁盤文件。讀取文件和寫入文件的操作都是直接對內(nèi)存進(jìn)行操作。
通過內(nèi)存映射文件,能夠更高效率地讀取文件,提高系統(tǒng)性能。
內(nèi)存映射文件的實(shí)現(xiàn)原理:
?實(shí)際上映射這個(gè)步驟就只有過程一了,映射完成后我們會(huì)得到一個(gè)地址,這個(gè)過程不存在數(shù)據(jù)拷貝。然后我們可以通過這個(gè)地址來像訪問內(nèi)存一樣訪問文件。但其實(shí)這個(gè)時(shí)候還不算真正完成了整個(gè)操作。在我們第一次對數(shù)據(jù)進(jìn)行訪問的時(shí)候,因?yàn)槲覀儾]將數(shù)據(jù)拷貝到內(nèi)存,所以會(huì)引發(fā)一個(gè)缺頁中斷,就是過程3,然后系統(tǒng)會(huì)將硬盤上的文件拷貝到內(nèi)存中。當(dāng)然并不會(huì)一次將文件全部的數(shù)據(jù)拷貝到內(nèi)存中,一般是我們需要用哪些數(shù)據(jù)的時(shí)候拷貝哪些數(shù)據(jù),并且數(shù)據(jù)是以4KB大小的頁為單位的。
更詳細(xì)的解釋可以看看這個(gè)博客:https://blog.csdn.net/mg0832058/article/details/5890688
除了用于加載文件,內(nèi)存映射文件也可以在同一臺計(jì)算機(jī)上運(yùn)行的多個(gè)進(jìn)程之間共享數(shù)據(jù),實(shí)現(xiàn)原理很簡單,只要將不同的進(jìn)程的間的共享數(shù)據(jù)頁提交到虛擬內(nèi)存的相同頁面就可以了。這樣,當(dāng)一個(gè)進(jìn)程改變了數(shù)據(jù)頁的內(nèi)容,通過分頁映射機(jī)制,其他進(jìn)程的共享數(shù)據(jù)區(qū)的內(nèi)容就會(huì)發(fā)生改變,因?yàn)樗鼈儗?shí)際在同一個(gè)地方。
使用內(nèi)存映射文件:
CreateFileMapping:
invoke CreateFileMapping,hFile,lpFileMappingAttributes,\flProtect,dwMaximumSizeHigh,dwMaximumSizeLow,lpName
.if eaxmov hFileMap,eax
.endif
該函數(shù)創(chuàng)建一個(gè)內(nèi)存映射文件對象,這個(gè)步驟決定了是在磁盤文件中建立建立內(nèi)存映射文件,還是在頁文件中建立進(jìn)程間的共享映射。
函數(shù)的第一個(gè)參數(shù)是我們要建立內(nèi)存映射的文件的句柄,也就是說我們得先打開一個(gè)文件得到它的句柄。如果是要建立一個(gè)位于頁文件中的內(nèi)存映射文件供不同進(jìn)程共享,那么這個(gè)參數(shù)指定為1。
第二個(gè)參數(shù)指向一個(gè)SECURITY_ATTRIBUTES結(jié)構(gòu),它用來定義內(nèi)存映射文件對象是否可繼承,不需要繼承的話就把這個(gè)參數(shù)設(shè)置為-1。
第三個(gè)參數(shù)指定該內(nèi)存映射我呢見的保護(hù)類型,它可以是下面取值:
- PAGE_READONLY:內(nèi)存映射文件提交的內(nèi)存頁面為只讀屬性
- PAGE_READWRITE:? 內(nèi)存映射文件提交的內(nèi)存是可讀寫的。
- PAGE_WRITECOPY:內(nèi)存映射文件提交的內(nèi)存可以有Copy on Write屬性。
dwMaximumSizeHigh和dwMaximumSizeLow參數(shù)組合指定了一個(gè)64位的內(nèi)存映射文件的長度。如果這個(gè)長度大于磁盤文件的長度,那么磁盤文件將被擴(kuò)展到這個(gè)長度,如果小于就只能取磁盤文件的一部分。如果設(shè)置為0,那么內(nèi)存映射文件的大小就會(huì)被自動(dòng)擴(kuò)展到磁盤文件的大小。
最后那個(gè)參數(shù)指定一個(gè)字符串,是用來給定內(nèi)存映射文件的名字的。如果是用于進(jìn)程間的共享內(nèi)存,那么這個(gè)名字就很有必要了,因?yàn)槠渌M(jìn)程建立映射的時(shí)候要用到這個(gè)名字。如果只是用于磁盤文件,那這個(gè)就沒有必要了。
OpenFileMapping:
invoke OpenFileMapping,dwDesiredAccess,bInheritHandle,lpName
.if eaxmov hFileMap,eax
.endif
該函數(shù)打開一個(gè)創(chuàng)建好的對象。最后一個(gè)參數(shù)就是之前的CreateFileMapping函數(shù)創(chuàng)建的內(nèi)存映射文件的對象的名字。然后第一個(gè)參數(shù)指定保護(hù)類型:
- FILE_MAP_WRITE:可寫屬性
- FILE_MAP_READ:可讀屬性
- FILE_MAP_COPY:Copy on Write屬性
MapViewOfFile:
invoke MapViewOfFile,hFileMap,dwDesiredAccess,\dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap
.if eaxmov lpMemory,eax
.endif
該函數(shù)用來映射內(nèi)存映射文件的一個(gè)視圖。我們之前創(chuàng)建或打開了一個(gè)內(nèi)存映射文件對象后還不算完,我們還得通過這個(gè)函數(shù)在進(jìn)程的地址空間中映射那個(gè)文件的視圖,該操作就是給需要映射的文件內(nèi)容分配線性地址空間,并將線性地址和文件內(nèi)容對應(yīng)起來。當(dāng)一個(gè)視圖被映射的時(shí)候,系統(tǒng)只是為它分配足以覆蓋文件視圖的連續(xù)地址空間,并不馬上將它提交到物理存儲(chǔ)器的文件中,只有第一次讀取內(nèi)存頁面的時(shí)候,系統(tǒng)才分配一個(gè)對應(yīng)于視圖頁面的物理內(nèi)存頁面。
第一個(gè)參數(shù)就是內(nèi)存映射文件函數(shù)對象句柄,后面一個(gè)參數(shù)也是指定保護(hù)類型,屬性氣質(zhì)==取值和上一個(gè)函數(shù)一樣。
參數(shù)dwFileOffsetHigh和參數(shù)dwFileOffsetLow組成一個(gè)64位的偏移,指出從視圖的基地址從文件的哪個(gè)位置開始映射。最后一個(gè)參數(shù)指定要映射的字節(jié)數(shù),指定為0的話就是整個(gè)文件,同時(shí)偏移地址被忽略。如果映射成功,函數(shù)返回一個(gè)指向文件開始位置的指針。然后我們就可以通過這個(gè)指針來訪問文件了。
UnmapViewOfFile:
invoke UnmapViewOfFile,lpMemory
該函數(shù)解除映射。
CloseHandle:
invoke CloseHandle,hFileMap
該函數(shù)關(guān)閉內(nèi)存映射文件對象句柄。
FlushViewOfFile:
invoke FlushViewOfFile,lpMemory,dwFileSize
該函數(shù)將從指定地址開始,指定大小的數(shù)據(jù)塊中的臟頁面寫到磁盤。
當(dāng)對視圖中的內(nèi)存進(jìn)行修改后,系統(tǒng)會(huì)在視圖撤銷映射或映射對象被刪除時(shí)自動(dòng)將數(shù)據(jù)寫到磁盤中。但是程序也可以用上面這個(gè)函數(shù)手動(dòng)寫入。
根據(jù)以上,我們可以寫一個(gè)共享內(nèi)存的小程序。
- 用OpenFileMapping函數(shù)打開一個(gè)命名的內(nèi)存映射文件對象,得到內(nèi)存映射文件對象句柄,如果打開成功則跳到3,不成功則跳到2.
- 用CreateFileMapping函數(shù)創(chuàng)建一個(gè)命名的內(nèi)存映射文件對象,得到內(nèi)存映射文件對象句柄。
- 用MapViewOfFile函數(shù)映射對象的一個(gè)視圖,得到指向映射到內(nèi)存的第一個(gè)字節(jié)的指針lpMemory。
- 用這個(gè)指針來對共享內(nèi)存進(jìn)行操作。
- UnmapViewOfFile解除視圖映射。
- CloseHandle關(guān)閉內(nèi)存映射文件。
因?yàn)檫@個(gè)是共享內(nèi)存,所以沒有打開文件那一步。下面是程序的資源腳本文件:
#include <resource.h>
#define ICO_MAIN 0x1000
#define DLG_MAIN 0x100
#define IDC_TXT 0x101
#define IDC_INFO 0x102ICO_MAIN ICON "Main.ico"
DLG_MAIN DIALOG 229,208,211,55
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "內(nèi)存映射文件共享"
FONT 9 , "宋體"
BEGINLTEXT "請執(zhí)行本程序的多個(gè)拷貝,并嘗試在下面輸入文本:",-1,7,8,196,8EDITTEXT IDC_TXT,7,22,197,12,ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOPLTEXT "",IDC_INFO,8,41,196,8
END
然后是程序的匯編源代碼:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;當(dāng)打開第一個(gè)文件,程序創(chuàng)建共享內(nèi)存映射文件,映射文件。打開第二個(gè)文件,打開共享內(nèi)存映射文件對象,映射文件,
;映射文件后可以直接通過函數(shù)返回的指針像訪問內(nèi)存一樣訪問函數(shù),而寫入內(nèi)存的數(shù)據(jù)會(huì)在
;當(dāng)在某一個(gè)文件輸入框輸入字符,程序檢測字符,設(shè)置共享內(nèi)存內(nèi)數(shù)據(jù)為輸入框內(nèi)容,每個(gè)進(jìn)程的函數(shù)的計(jì)時(shí)器
;都發(fā)射消息,將共享內(nèi)存映射文件中的數(shù)據(jù)放入對話框中顯示。
;當(dāng)有進(jìn)程結(jié)束,進(jìn)程釋放句柄,解除視圖映射,并不用擔(dān)心其他進(jìn)程會(huì)收到影響,因?yàn)閃INDOWS中有計(jì)數(shù)器,每當(dāng)開啟一個(gè)進(jìn)程
;計(jì)數(shù)器加一,結(jié)束一個(gè)進(jìn)程計(jì)數(shù)器減一,所以只有當(dāng)進(jìn)程全部結(jié)束系統(tǒng)才會(huì)釋放內(nèi)存映射文件
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.386.model flat,stdcalloption casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;Include文件
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;Equ
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ 1000h
DLG_MAIN equ 100h
IDC_TXT equ 101h
IDC_INFO equ 102h
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;數(shù)據(jù)段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.data?
hInstance dd ?
hWinMain dd ?
hFileMap dd ?
lpMemory dd ?.const
szErr db '無法建立內(nèi)存共享文件',0
szMMFName db 'MMF_Share_Example',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;代碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CreateMMF procinvoke OpenFileMapping,FILE_MAP_READ or FILE_MAP_WRITE,\ ;打開內(nèi)存映射文件對象,返回內(nèi)存映射文件句柄0,addr szMMFName.if ! eaxinvoke CreateFileMapping,-1,NULL,PAGE_READWRITE,0,\ ;如果之前沒有創(chuàng)建對象,則創(chuàng)建一個(gè)4096,addr szMMFName.if ! eaxjmp @F.endif.endifmov hFileMap,eaxinvoke MapViewOfFile,eax,FILE_MAP_READ or FILE_MAP_WRITE,\ ;映射整個(gè)文件,返回內(nèi)存地址0,0,0.if eaxmov lpMemory,eax;mov dword ptr [eax],0ret.endifinvoke CloseHandle,hFileMap
@@:invoke MessageBox,hWinMain,addr szErr,NULL,MB_OKinvoke EndDialog,hWinMain,FALSEret_CreateMMF endp_CloseMMF procinvoke UnmapViewOfFile,lpMemory ;解除視圖映射invoke CloseHandle,hFileMap ;關(guān)閉句柄mov lpMemory,0mov hFileMap,0ret
_CloseMMF endp_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParamlocal @szBuffer[4096]:bytemov eax,wMsg.if eax == WM_TIMERinvoke SetDlgItemText,hWnd,IDC_INFO,lpMemory.elseif eax == WM_CLOSEinvoke KillTimer,hWnd,1invoke _CloseMMFinvoke EndDialog,hWinMain,0.elseif eax == WM_INITDIALOGpush hWndpop hWinMaininvoke LoadIcon,hInstance,ICO_MAINinvoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eaxinvoke SendDlgItemMessage,hWnd,IDC_TXT,\EM_SETLIMITTEXT,100,0invoke _CreateMMFinvoke SetTimer,hWnd,1,100,NULL;設(shè)置一個(gè)計(jì)時(shí)器.elseif eax == WM_COMMANDmov eax,wParam.if ax == IDC_TXT && [lpMemory] ;如果輸入欄有變化invoke GetDlgItemText,hWnd,IDC_TXT,\ ;獲取輸入欄內(nèi)容,存入共享內(nèi)存映射文件lpMemory,4096.endif.elsemov eax,FALSEret.endifmov eax,TRUEret
_ProcDlgMain endpstart:invoke GetModuleHandle,NULLmov hInstance,eaxinvoke DialogBoxParam,hInstance,DLG_MAIN,NULL,\offset _ProcDlgMain,NULLinvoke ExitProcess,NULL
end start
在一個(gè)窗口輸入,其他窗口都會(huì)跟著變化。