windows进程间通信系列 第二篇 内存映射文件

1.内存文件映射的原理

内存映射文件是一个文件到一块内存的映射,Win32提供了允许应用程序把文件映射到一个进程的函数 (CreateFileMapping)。在Win32进程的虚拟地址空间中保留一段内存区域,把目标文件映射到这段虚拟内存之中。

内存映射文件与虚拟内存有一些相同点与区别。内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。而虚拟内存的物理存储器来自于页交换文件。

与虚拟内存一样,完成硬盘中页交换文件到虚拟地址映射需要一些数据结构或硬件,在内存映射文件与磁盘中存在的文件进行映射的时候也需要一些数据结构来辅助。

对于这系统转换的机制可以参考:

点击打开链接

内存映射文件的原理与实现

2.内存映射文件使用的API

CreateFile,CreateFileMapping,MapViewOfFile(Ex) ,OpenFileMapping。


(1)CreateFile

 HANDLE CreateFile(
  LPCTSTR lpFileName, //指向文件名的指针
  DWORD dwDesiredAccess, //访问模式(写/读)
  DWORD dwShareMode, //共享模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
  DWORD dwCreationDisposition, //如何创建
  DWORD dwFlagsAndAttributes, //文件属性
  HANDLE hTemplateFile //用于复制文件句柄
  );

参数列表
  lpFileName String 要打开的文件的名字
  dwDesiredAccess Long 如果为 GENERIC_READ 表示允许对设备进行读访问;如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);如果为零,表示只允许获取与一个设备有关的信息
  dwShareMode Long, 零表示不共享; FILE_SHARE_READ 和/或 FILE_SHARE_WRITE 表示允许对文件进行共享访问
  lpSecurityAttributes SECURITY_ATTRIBUTES, 指向一个SECURITY_ATTRIBUTES结构的指针,定义了文件的安全特性(如果操作系统支持的话)
  dwCreationDisposition Long,下述常数之一:
  CREATE_NEW 创建文件;如文件存在则会出错
  CREATE_ALWAYS 创建文件,会改写前一个文件
  OPEN_EXISTING 文件必须已经存在。由设备提出要求
  OPEN_ALWAYS 如文件不存在则创建它
  TRUNCATE_EXISTING 讲现有文件缩短为零长度
  dwFlagsAndAttributes Long, 一个或多个下述常数
  FILE_ATTRIBUTE_ARCHIVE 标记归档属性
  FILE_ATTRIBUTE_COMPRESSED 将文件标记为已压缩,或者标记为文件在目录中的默认压缩方式
  FILE_ATTRIBUTE_NORMAL 默认属性
  FILE_ATTRIBUTE_HIDDEN 隐藏文件或目录
  FILE_ATTRIBUTE_READONLY 文件为只读
  FILE_ATTRIBUTE_SYSTEM 文件为系统文件
  FILE_FLAG_WRITE_THROUGH 操作系统不得推迟对文件的写操作
  FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作
  FILE_FLAG_NO_BUFFERING 禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块
  FILE_FLAG_RANDOM_ACCESS 针对随机访问对文件缓冲进行优化
  FILE_FLAG_SEQUENTIAL_SCAN 针对连续访问对文件缓冲进行优化
  FILE_FLAG_DELETE_ON_CLOSE 关闭了上一次打开的句柄后,将文件删除。特别适合临时文件
  也可在Windows NT下组合使用下述常数标记:
  SECURITY_ANONYMOUS, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION, SECURITY_DELEGATION, SECURITY_CONTEXT_TRACKING, SECURITY_EFFECTIVE_ONLY
  hTemplateFile Long, 如果不为零,则指定一个文件句柄。新文件将从这个文件中复制扩展属性

返回值

  如执行成功,则返回文件句柄。

  INVALID_HANDLE_VALUE表示出错,会设置GetLastError。即使函数成功,但若文件存在,且指定了CREATE_ALWAYS 或 OPEN_ALWAYS,GetLastError也会设为ERROR_ALREADY_EXISTS


(2)CreateFileMapping

HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全设置
DWORD flProtect, //保护设置
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享内存名称
);
参数说明:

1) 物理 文件句柄
任何可以获得的物理 文件句柄, 如果你需要创建一个物理文件无关的内存映射也无妨, 将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了。
如果需要和物理文件关联, 要确保你的物理文件创建的时候的访问模式和"保护设置"匹配, 比如: 物理文件只读, 内存映射需要读写就会发生错误。 推荐你的物理文件使用独占方式创建。
如果使用 INVALID_HANDLE_VALUE, 也需要设置需要申请的内存空间的大小, 无论物理 文件句柄参数是否有效, 这样 CreateFileMapping 就可以创建一个和物理文件大小无关的内存空间给你, 甚至超过实际文件大小, 如果你的物理文件有效, 而大小参数为0, 则返回给你的是一个和物理文件大小一样的内存空间地址范围。 返回给你的文件映射 地址空间是可以通过复制, 集成或者命名得到, 初始内容为0。
2) 保护设置
就是安全设置, 不过一般设置NULL就可以了, 使用默认的安全配置。 在win2k下如果需要进行限制, 这是针对那些将内存文件映射共享给整个网络上面的应用进程使用时, 可以考虑进行限制。
3) 高位文件大小
亲们, 我想目前我们的机器都是32位的东东, 不可能得到超过32位进程所能寻址的私有32位 地址空间, 一般还是设置0吧, 我没有也不想尝试将它设置超过0的情况。
4) 低位文件大小
这个还是可以进行设置的, 不过为了让其他共享用户知道你申请的文件映射的相关信息, 我使用的时候是在获得的 地址空间头部添加一个结构化描述信息, 记录内存映射的大小, 名称等, 这样实际申请的空间就比输入的增加了一个头信息结构大小了, 我认为这样类似BSTR的方式应该是比较合理的。
5) 共享内存名称
如果为了对内存进行互斥访问,设置了一个互斥句柄, 而名称选择和命名 共享内存同名, 他们使用共同的namespace,会导致错误。
6) 调用CreateFileMapping的时候GetLastError的对应错误
ERROR_FILE_INVALID 如果企图创建一个零长度的文件映射, 应有此报
ERROR_INVALID_HANDLE 如果发现你的命名内存空间和现有的内存映射, 互斥量, 信号量临界区同名就麻烦了
ERROR_ALREADY_EXISTS 表示内存空间命名已经存在
7) 相关服务或者平台的命名保留Terminal Services:
命名可以包含 "Global" 或者 "Local" 前缀在全局或者会话 名空间初级文件映射。 其他部分可以包含任何除了()以外的字符, 可以参考 Kernel Object Name Spaces。
(以上摘自 百度百科)

(3)MapViewOfFile
将一个文件映射对象映射到当前应用程序的 地址空间。MapViewOfFileEx允许我们指定一个基本地址来进行映射。

LPVOID WINAPI MapViewOfFile(
  __in HANDLE hFileMappingObject,   // 文件映像句柄
  __in DWORD dwDesiredAccess,       // 映像的访问方式
  __in DWORD dwFileOffsetHigh,       
  __in DWORD dwFileOffsetLow,
  __in SIZE_T dwNumberOfBytesToMap  // 映射多少字节到内存
  );
LPVOID WINAPI MapViewOfFileEx(
  __in HANDLE hFileMappingObject,
  __in DWORD dwDesiredAccess,
  __in DWORD dwFileOffsetHigh,
  __in DWORD dwFileOffsetLow,
  __in SIZE_T dwNumberOfBytesToMap,
  __in LPVOID lpBaseAddress
  );

返回值:Long,文件映射在内存中的起始地址。零表示出错。会设置GetLastError

参数表:
hFileMappingObject Long,文件映射对象的句柄。
dwDesiredAccess Long,下述常数之一:
FILE_MAP_WRITE 映射可读可写。文件映射对象必须通过PAGE_READWRITE访问创建。
FILE_MAP_READ 映射只读。文件映射对象必须通过PAGE_READ 或 PAGE_READWRITE访问创建。
FILE_MAP_ALL_ACCESS 与FILE_MAP_WRITE相同。
FILE_MAP_COPY 映射时保留写操作的副本。文件映射对象必须用PAGE_WRITECOPY访问在win95下创建
dwFileOffsetHigh Long,文件中映射起点的高32位地址。
dwFileOffsetLow Long,文件中映射起点的低32位地址。
dwNumberOfBytesToMap Long,文件中要映射的字节数。用零映射整个文件映射对象。
lpBaseAddress Long,指定映射文件映射对象的地址。如这个地址处没有足够的内存空间,那么对MapViewOfFileEx的调用会失效。零表示允许windows寻找一个地址。

参数详解:

参数1:hFileMappingObject 为CreateFileMapping()返回的文件映像对象句柄。
参数2:dwDesiredAccess 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。 可取以下值:
FILE_MAP_ALL_ACCESS 等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ. 文件映射对象被创建时必须指定PAGE_READWRITE 选项.
FILE_MAP_COPYA 可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性.
FILE_MAP_EXECUTE 可以将文件中的数据作为代码来执行.在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性.
FILE_MAP_READ 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性.
FILE_MAP_WRITEA 可以读取和写入文件.在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性.
参数3:dwFileOffsetHigh 表示文件映射起始偏移的高32位.
参数4:dwFileOffsetLow 表示文件映射起始偏移的低32位.(64KB对齐不是必须的)
参数5:dwNumberOfBytes 指定映射文件的字节数.



(4)OpenFileMapping

打开一个现成的文件映射对象

返回值:Long,指定文件映射对象的句柄。零表示出错,可以设置GetLastError。

参数:

dwDesiredAccess Long,带有前缀FILE_MAP_???的一个常数。参考MapViewOfFile函数dwDesiredAccess参数的说明。
bInheritHandle Long,如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。
lpName String,指定要打开的文件映射对象名称。


#include <windows.h>
#include <stdio.h>

int SendData()
{
	HANDLE hFile = CreateFile( "test.txt" , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ|FILE_SHARE_WRITE , 
		NULL , OPEN_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
	if( INVALID_HANDLE_VALUE == hFile )
	{
		GetLastError() ;
		return -1 ;
	}

	HANDLE hFileMap = CreateFileMapping( hFile , NULL , PAGE_READWRITE , 0 , 4096 , "share_file" ) ;
	if( INVALID_HANDLE_VALUE == hFileMap )
	{
		GetLastError() ;
		return -1 ;
	}

	LPSTR pFileAddr = (LPSTR)MapViewOfFile( hFileMap , PAGE_READWRITE , 0 , 0 , 0 ) ;

	printf("0x%x\n" , pFileAddr ) ;

	return 1 ;

}

int RecvData()
{
	HANDLE hFileMap = OpenFileMapping( FILE_MAP_READ , NULL , "share_file" ) ;
	if( INVALID_HANDLE_VALUE == hFileMap )
	{
		GetLastError() ;
		return -1 ;
	}

	LPSTR ptr = (LPSTR)MapViewOfFile( hFileMap , PAGE_READWRITE , 0 , 0 , 0 ) ;

	char dstData[4096] = { 0 } ;
	memcpy( dstData , ptr , 4096 ) ;
	printf("%s\n" , dstData ) ;

	return 1 ;
}

int main()
{
	int bRet = SendData() ;

	bRet = RecvData() ;

	return 1 ;
}

上面代码的作用是:SendData函数创建文件test.txt文件的一个内存映射文件的对象,然后RecvData函数读取这个映像中的数据,输出来。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值