江苏 网站 备案网络营销公司全网推广公司
内存映射文件概念:
通过内存映射文件函数,可以将磁盘上的文件的全部或部分映射到进程的虚拟地址空间的某个位置,一旦完成了映射,就可以通过指针像访问内存一样访问磁盘文件。读取文件和写入文件的操作都是直接对内存进行操作。
通过内存映射文件,能够更高效率地读取文件,提高系统性能。
内存映射文件的实现原理:
实际上映射这个步骤就只有过程一了,映射完成后我们会得到一个地址,这个过程不存在数据拷贝。然后我们可以通过这个地址来像访问内存一样访问文件。但其实这个时候还不算真正完成了整个操作。在我们第一次对数据进行访问的时候,因为我们并没将数据拷贝到内存,所以会引发一个缺页中断,就是过程3,然后系统会将硬盘上的文件拷贝到内存中。当然并不会一次将文件全部的数据拷贝到内存中,一般是我们需要用哪些数据的时候拷贝哪些数据,并且数据是以4KB大小的页为单位的。
更详细的解释可以看看这个博客:https://blog.csdn.net/mg0832058/article/details/5890688
除了用于加载文件,内存映射文件也可以在同一台计算机上运行的多个进程之间共享数据,实现原理很简单,只要将不同的进程的间的共享数据页提交到虚拟内存的相同页面就可以了。这样,当一个进程改变了数据页的内容,通过分页映射机制,其他进程的共享数据区的内容就会发生改变,因为它们实际在同一个地方。
使用内存映射文件:
CreateFileMapping:
invoke CreateFileMapping,hFile,lpFileMappingAttributes,\flProtect,dwMaximumSizeHigh,dwMaximumSizeLow,lpName
.if eaxmov hFileMap,eax
.endif
该函数创建一个内存映射文件对象,这个步骤决定了是在磁盘文件中建立建立内存映射文件,还是在页文件中建立进程间的共享映射。
函数的第一个参数是我们要建立内存映射的文件的句柄,也就是说我们得先打开一个文件得到它的句柄。如果是要建立一个位于页文件中的内存映射文件供不同进程共享,那么这个参数指定为1。
第二个参数指向一个SECURITY_ATTRIBUTES结构,它用来定义内存映射文件对象是否可继承,不需要继承的话就把这个参数设置为-1。
第三个参数指定该内存映射我呢见的保护类型,它可以是下面取值:
- PAGE_READONLY:内存映射文件提交的内存页面为只读属性
- PAGE_READWRITE: 内存映射文件提交的内存是可读写的。
- PAGE_WRITECOPY:内存映射文件提交的内存可以有Copy on Write属性。
dwMaximumSizeHigh和dwMaximumSizeLow参数组合指定了一个64位的内存映射文件的长度。如果这个长度大于磁盘文件的长度,那么磁盘文件将被扩展到这个长度,如果小于就只能取磁盘文件的一部分。如果设置为0,那么内存映射文件的大小就会被自动扩展到磁盘文件的大小。
最后那个参数指定一个字符串,是用来给定内存映射文件的名字的。如果是用于进程间的共享内存,那么这个名字就很有必要了,因为其他进程建立映射的时候要用到这个名字。如果只是用于磁盘文件,那这个就没有必要了。
OpenFileMapping:
invoke OpenFileMapping,dwDesiredAccess,bInheritHandle,lpName
.if eaxmov hFileMap,eax
.endif
该函数打开一个创建好的对象。最后一个参数就是之前的CreateFileMapping函数创建的内存映射文件的对象的名字。然后第一个参数指定保护类型:
- 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
该函数用来映射内存映射文件的一个视图。我们之前创建或打开了一个内存映射文件对象后还不算完,我们还得通过这个函数在进程的地址空间中映射那个文件的视图,该操作就是给需要映射的文件内容分配线性地址空间,并将线性地址和文件内容对应起来。当一个视图被映射的时候,系统只是为它分配足以覆盖文件视图的连续地址空间,并不马上将它提交到物理存储器的文件中,只有第一次读取内存页面的时候,系统才分配一个对应于视图页面的物理内存页面。
第一个参数就是内存映射文件函数对象句柄,后面一个参数也是指定保护类型,属性气质==取值和上一个函数一样。
参数dwFileOffsetHigh和参数dwFileOffsetLow组成一个64位的偏移,指出从视图的基地址从文件的哪个位置开始映射。最后一个参数指定要映射的字节数,指定为0的话就是整个文件,同时偏移地址被忽略。如果映射成功,函数返回一个指向文件开始位置的指针。然后我们就可以通过这个指针来访问文件了。
UnmapViewOfFile:
invoke UnmapViewOfFile,lpMemory
该函数解除映射。
CloseHandle:
invoke CloseHandle,hFileMap
该函数关闭内存映射文件对象句柄。
FlushViewOfFile:
invoke FlushViewOfFile,lpMemory,dwFileSize
该函数将从指定地址开始,指定大小的数据块中的脏页面写到磁盘。
当对视图中的内存进行修改后,系统会在视图撤销映射或映射对象被删除时自动将数据写到磁盘中。但是程序也可以用上面这个函数手动写入。
根据以上,我们可以写一个共享内存的小程序。
- 用OpenFileMapping函数打开一个命名的内存映射文件对象,得到内存映射文件对象句柄,如果打开成功则跳到3,不成功则跳到2.
- 用CreateFileMapping函数创建一个命名的内存映射文件对象,得到内存映射文件对象句柄。
- 用MapViewOfFile函数映射对象的一个视图,得到指向映射到内存的第一个字节的指针lpMemory。
- 用这个指针来对共享内存进行操作。
- UnmapViewOfFile解除视图映射。
- CloseHandle关闭内存映射文件。
因为这个是共享内存,所以没有打开文件那一步。下面是程序的资源脚本文件:
#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 "内存映射文件共享"
FONT 9 , "宋体"
BEGINLTEXT "请执行本程序的多个拷贝,并尝试在下面输入文本:",-1,7,8,196,8EDITTEXT IDC_TXT,7,22,197,12,ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOPLTEXT "",IDC_INFO,8,41,196,8
END
然后是程序的汇编源代码:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;当打开第一个文件,程序创建共享内存映射文件,映射文件。打开第二个文件,打开共享内存映射文件对象,映射文件,
;映射文件后可以直接通过函数返回的指针像访问内存一样访问函数,而写入内存的数据会在
;当在某一个文件输入框输入字符,程序检测字符,设置共享内存内数据为输入框内容,每个进程的函数的计时器
;都发射消息,将共享内存映射文件中的数据放入对话框中显示。
;当有进程结束,进程释放句柄,解除视图映射,并不用担心其他进程会收到影响,因为WINDOWS中有计数器,每当开启一个进程
;计数器加一,结束一个进程计数器减一,所以只有当进程全部结束系统才会释放内存映射文件
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.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
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.data?
hInstance dd ?
hWinMain dd ?
hFileMap dd ?
lpMemory dd ?.const
szErr db '无法建立内存共享文件',0
szMMFName db 'MMF_Share_Example',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CreateMMF procinvoke OpenFileMapping,FILE_MAP_READ or FILE_MAP_WRITE,\ ;打开内存映射文件对象,返回内存映射文件句柄0,addr szMMFName.if ! eaxinvoke CreateFileMapping,-1,NULL,PAGE_READWRITE,0,\ ;如果之前没有创建对象,则创建一个4096,addr szMMFName.if ! eaxjmp @F.endif.endifmov hFileMap,eaxinvoke MapViewOfFile,eax,FILE_MAP_READ or FILE_MAP_WRITE,\ ;映射整个文件,返回内存地址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 ;关闭句柄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;设置一个计时器.elseif eax == WM_COMMANDmov eax,wParam.if ax == IDC_TXT && [lpMemory] ;如果输入栏有变化invoke GetDlgItemText,hWnd,IDC_TXT,\ ;获取输入栏内容,存入共享内存映射文件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
在一个窗口输入,其他窗口都会跟着变化。