FileMapping漫谈

来源:互联网 发布:京东运营和淘宝运营 编辑:程序博客网 时间:2024/06/05 18:47

内存映射问题

0x01、内存映射:

• 在32位的Windows系统中,每一个进程都有权访问他自己的4GB(232=4294967296)平面地址空间,没有段,没有选择符,没有near和far指针,没有near和far函数调用,也没有内存模式。

• 每个进程都有独立的4GB逻辑地址空间,32位的Windows系统允许每一个进程独立访问自己的内存,即独立于其它进程,也即它自己的32位逻辑地址空间。操作系统将把每一个进程的逻辑地址转换成实际的物理地址,独立的地址空间可以使其他已经出错的进程之间相互隔离,入阁一个进程通过他自己的内存空间处理数据,其他的进程就比在DOS中安全,在DOS中的所有应用程序共享相同的物理内存空间,虽然这带来了许多好处,但在不同进程之间转的指针,就会出现一些麻烦。在一个进程中,一个给定的逻辑地址将与另一进程的指针不会有相同的逻辑地址。

• 那么怎么样才能在32位的Windows系统中达到共享内存的目的呢?

• 随着进程的分离内存空间的出现,进程不能简单地使用GlobalAlloc()函数来分配内存,并把它转递给另一个进程来共享,一个进程检查有另一个进程分配的指针,他只是指向随机地址。然而,在32位的Windows系统支持在进程间共享内存映象文件,可以通过内存映象文件来达到内存共享的目的。

• 32位的Windows系统的虚拟内存系统具有把实际内存映象到页面文件或交换文件的能力.通过把内存映象到任何想映象的文件,包括系统页面本身,应用程序就可以拓展这种能力。文件影响能用来提供更快更简捷的文件访问方式,并提供内存共享。

• 把文件映像到内存,首先必须调用CreateFileMapping()函数,然后再调用MapViewOfFile函数,把文件视映像到进程地址空间上。

0x02、内存映射目的:

• 系统使用内存映射文件,以便加载和执行. e x e和D L L文件。这可以大大节省页文件空间和应用程序启动运行所需的时间。

• 可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行I / O操作,并且可以不必对文件内容进行缓存。

• 可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。Wi n d o w s确实提供了其他一些方法,以便在进程之间进行数据通信,但是这些方法都是使用内存映射文件来实现的,这使得内存映射文件成为单个计算机上的多个进程互相进行通信的最有效的方法。

这里给出一个抽象图:

这里写图片描述

0x03、内存映射函数

内存映射文件的函数包括

CreateFileMapping , OpenFileMapping, MapViewOfFile, UnmapViewOfFile和 FlushViewOfFile。

0x01.CreateFileMapping函数

HANDLE CreateFileMapping(    HANDLE hFile,                      // 一个文件句柄    LPSECURITY_ATTRIBUTE lpAttributes, // 定义内存映射文件对象是否可以被承    DWORD flProtect,                   // 该内存映射文件的保护类型    DWORD dwMaximumSizeHigh,           // 内存映射文件的长度    DWORD dwMaximumSizeLow,            //最小长度    LPCTSTR lpName                     // 内存映射文件的名字)

• hFile 指定要映射的文件的句柄,如果这是一个已经打开的文件的句柄(CreateFile函数的返回值),那么将建立这个文件的内存映射文件,如果这个参数为-1,则建立共享内存。

• lpAttribute 安全属性,一般设为NULL

• flProtect 指定映射文件的保护类型,它的取值可以是PAGE_READONLY(内存页面只读)或 PAGE_READWRITE(内存页面可读写)。

• dwMaximumSizeHigh 和 dwMaximumSizeLow参数组合指定了一个64位的内存映射文件的长度。一种简单的方法是将这两个参数全部设置为0,那么内存映射文件的大小将与磁盘文件大小一致。

0x02.OpenFileMapping函数

HANDLE OpenFileMapping(    DWORD dwDesiredAccess,    // 指定保护类型    BOOL  bIsInheritHandle,   // 返回的句柄是否可以被继承    LPCSTR lpName             // 创建对象时使用的名字

• 如果创建的是共享内存,其他进程不能再使用CreateFileMapping函数去创建同名的内存映射文件对象,而要使用OpenFileMapping函数打开已创建好的对象。

• dwDesiredAcess 指定保护类型有FILE_MAP_WRITE或FILE_MAP_READ

0x03.MapViewOfFile函数

LPVOID MapViewOfFile(    HANDLE  hFileMappingObject,   // 前两个函数返回的内存映射文件的句柄    DWORD  dwDesiredAcess,        // 保护类型FILE_MAP_READ ,FILE_MAP_WRITE    DWORD  dwFileOffsetHight,     // 从文件的那个地址开始映射    DWORD  dwFileOffsetLow,    SIZE_T   dwNumberOfBytesToMap // 要映射的字节数,为0则映射整个文件

0x04.UnmapViewOfFile函数

BOOL UnmapViewOfFile( LPCVOID  lpBaseAddress)

• 当不再使用内存映射文件时,可以通过UmmapViewOfFile函数撤销映射并使用CloseHandle函数关闭内存映射文件的句柄。

0x05.FlushViewOfFile函数

BOOL FlushViewOfFile(    LPCVOID lpBaseAddress,         // 开始的地址    SIZE_T  dwNumberOfBytesToFlush // 数据块的大小

• 如果修改了映射视图中的内存,系统会在试图撤销映射或文件映射对象被删除时自动将数据写到磁盘上,但程序也可以根据需要将视图中的数据立即写到磁盘上。

0x04、使用步骤

1. 使用CreateFileMapping创建一个内存映射文件内核对象,告诉操作系统内存映射文件需要的物理内存大小,这个步骤决定了内存映射文件的用途――究竟是为磁盘上的文件建立内存映射还是为多个进程共享数据建立共享内存。或者使用OpenFileMapping打开映射文件内核对象。

2. 映射文件映射对象的全部或一部分到进程的地址空间,可以认为该操作是为文件中的内容分配线型地址空间,并将线型地址和文件内容对应起来,完成该操作的函数是MapViewOfFile。

0x01、使用内存映射文件读文件的具体过程可以这样:

(1) 调用CreateFile函数打开想要映射的文件,得到文件句柄hFile。

(2) 调用CreateFileMapping函数,并传入文件句柄hFile,为该文件创建一个内存映射内核对象,得到内存映射文件的句柄hMap。

(3) 调用MapViewOfFile函数映射整个文件或一部分到进程的虚拟地址空间。该函数返回文件映射到内存后的起始地址。使用指向这个地址的指针就可以读取文件的内容了。

(4) 调用UnmapViewOfFile函数来解除文件映射。

(5) 调用CloseHandle函数关闭文件对象,必须传入内存映射文件句柄hMap

(6) 调用CloseHandle函数关闭文件对象,必须传入文件句柄hFile。

0x02、进程间共享内存:

• 共享内存主要是通过映射机制实现的。Windows下进程的地址空间是相互隔离的,但在物理上却是重叠的。所谓的重叠是指同一块内存区域可能被多个进程同时使用。当调用CreateFileMapping创建命名的内存映射文件对象时,Windows即在物理内存中申请了一块指定大小的内存区域,返回文件映射对象的新句柄hMap。为了能够访问这块区域必须调MapViewOfiFile函数,促使Windows将此内存空间映射到进程的地址空间中。当在其他进程中访问这块区域时,则必须使用OpenFileMapping函数来取得对象句柄hMap,并调用MapViewOfFile函数得到此内存空间的一个映射。这样一来,系统就把同一块内存区域映射到了不同进程的地址空间中,从而达到共享内存的目的。

示例一:

#define  BUF_SIZE 256#define  FILE_MAP_NAME  "Global//{42561571-5A43-4c5c-9D76-4132D70A8968}"The first process:HANDLE hFile = CreateFile("c://mapping.txt",  GENERIC_READ|GENERIC_WRITE,  FILE_SHARE_READ|FILE_SHARE_WRITE,  NULL,  OPEN_EXISTING,  FILE_ATTRIBUTE_NORMAL,  NULL);if ( !hFile ){  return;}HANDLE hMapFile = NULL;hMapFile = CreateFileMapping( hFile,  NULL ,  PAGE_READWRITE,  0,            //一定为0 否则磁盘空间不足  BUF_SIZE,     //不能为0  FILE_MAP_NAME //Global/" or "Local/" 其他都不能带有  );//hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE,/*(HANDLE)0xFFFFFFFF*/// //NULL,// ShareRestrictedSD.GetSA(), // PAGE_READWRITE, // 0,// BUF_SIZE,// FILE_MAP_NAME// );if ( hMapFile == NULL ){  DWORD dwErr = GetLastError();  return;}void* pMapBuf = NULL;if ((pMapBuf = MapViewOfFile(hMapFile,  FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE)) == NULL){  DWORD dwErr = GetLastError();  return;}memset(pMapBuf, 0, 100);strcpy( (char *)pMapBuf, "create file text" );UnmapViewOfFile( pMapBuf );//CloseHandle( hMapFile );CloseHandle( hFile );The Second Process:HANDLE hMapFile = NULL;if ( (hMapFile = OpenFileMapping(  FILE_MAP_ALL_ACCESS,  FALSE,  //NULL,  FILE_MAP_NAME  )) == NULL){  DWORD dwErr = GetLastError();  return;}void* pMapBuf = NULL;if ( ( pMapBuf = MapViewOfFile(hMapFile,  FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE)) == NULL){  DWORD dwErr = GetLastError();  return;}strcpy( (char*)pMapBuf, "open file text");UnmapViewOfFile( pMapBuf );CloseHandle( hMapFile );

示例二

• 要先使用函数CreateFileMapping来创建一个想共享的文件数据句柄,然后使用MapViewOfFile来获取共享的内存地址,然后使用OpenFileMapping函数在另一个进程里打开共享文件的名称,这样就可以实现不同的进程共享数据

void FileMapping(void){              //打开共享的文件对象。     m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE,_T("TestFileMap"));     if (m_hMapFile)      {         //显示共享的文件数据。         LPTSTR lpMapAddr = (LPTSTR)MapViewOfFile(m_hMapFile,FILE_MAP_ALL_ACCESS,  0,0,0);         OutputDebugString(lpMapAddr);     }    else    {         //创建共享文件。         m_hMapFile = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,                 PAGE_READWRITE,0,1024, _T("TestFileMap"));      //拷贝数据到共享文件里。         LPTSTR lpMapAddr = (LPTSTR)MapViewOfFile(m_hMapFile,FILE_MAP_ALL_ACCESS, 0,0,0);         std::wstring strTest(_T("TestFileMap"));         wcscpy(lpMapAddr,strTest.c_str());         FlushViewOfFile(lpMapAddr,strTest.length()+1);     } } 
0 0
原创粉丝点击