一、引言

在Windows程序中,各个进程之常常需要交换数据,据通WIN32 API提供了多函使我方便高效的的通,通过这些函可以控制不同据交

(即:同机通)据交有多方式:消息、共享存、匿名(命名)管道、槽、Windows套接字等多共享(shared memory)可以定义为对以上的程是可存或存在于多个进程的虚拟地址空。例如:如果两个进程使用相同的DLL,只把DLL的代码页装入存一次,其他所有映射这个DLL程只要共享些代码页就可以了;利用消息机制实现IPC然有交据量小、携的信息少等缺点,但由于其实现方便、活而广泛用于无大量、据交程通之中。

二、共享内存的介绍

1、共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

2、为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。

3、由于多个进程共享一段内存,因此也需要依靠某种同步机制。

共享内存的特点:

Windows环境下共享内存通信_进程间通讯

三、同机共享存的实现

采用存映射文件实现WIN32的通Windows中的存映射文件的机制高效地操作文件提供了一WIN32程中保留一段域,把硬文件上的目文件映射到虚拟内存中。注意:在程序实现中必程之的同步问题

具体实现如下:

1、服务器创建一个共享内存。

在服器端程中存映射APICreateFileMapping建一有名字标识的共享存。

与虚拟内似,保方式参数可以是PAGE_READONLY或是PAGE_READWRITE。如果多程都同一共享写访问保持相互同步。映射文件可以指定PAGE_WRITECOPY志,可以保其原始据不遭到破坏,同其他程在必要自由的操作据的拷

例:建一"liuker"4096的有名映射文件:

HANDLEhmap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL,

       PAGE_READWRITE | SEC_COMMIT, 0, 0x1000, "liuker");

2、服务器映射到当前进程的地址空间。

建文件映射象后,服器端MapViewOfFile映射到本程的地址空间内

例:映射区视图:

LPVOIDlpdata = MapViewOfFile(hmap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);

3、客户端打指定的文件映射

例如

HANDLEhmapfile = OpenFileMappingA(FILE_MAP_READ, FALSE, "liuker");

4、客户端将一个文件映射对象映射到当前应用程序的地址空间。

例如:映射区视图

LPVOIDlpbase = MapViewOfFile(hmapfile, FILE_MAP_READ, 0, 0, 0);

5、UnmapViewOfFile撤消地址空间内的视图。

户进束使用共享存后,UnmapViewOfFile以取消其地址空间内视图

6、关闭映射。

四、使用文件映射实现共享内存。

FileMapping用于将存在于磁盘的文件放进一个进程的虚拟地址空间,并在该进程的虚拟地址空间中产生一个区域用于“存放”该文件,这个空间就叫做File View(存放在进程的虚拟内存中),系统并同时产生一个File MappingObject(存放于物理内存中)用于维持这种映射关系,这样当多个进程需要读写那个文件的数据时,它们的FileView其实对应的都是同一个File Mapping Object,这样做可节省内存和保持数据的同步性,并达到数据共享的目的。

当然在一个应用向文件中写入数据时,其它进程不应该去读取这个正在写入的数据。这就需要进行一些同步的操作。下边来看一下具体的API。

CreateFileMaping的用法:

HANDLECreateFileMapping( //返回FileMapping Object的句柄

HANDLE hFile,//想要产生映射的文件的句柄

LPSECURITY_ATTRIBUTESlpAttributes, //安全属性(只对NT和2000生效)

DWORDflProtect, //保护标致

DWORD dwMaximumSizeHigh,//在DWORD的高位中存放

File MappingObject //的大小

DWORDdwMaximumSizeLow, //在DWORD的低位中存放

File MappingObject,//的大小(通常这两个参数有一个为0)

LPCTSTR lpName//File Mapping Object的名称。

);

1)物理文件句柄

任何可以获得的物理文件句柄,如果你需要创建一个物理文件无关的内存映射也无妨,将它设置成为0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了。

如果需要和物理文件关联,要确保你的物理文件创建的时候的访问模式和"保护设置"匹配,比如:物理文件只读,内存映射需要读写就会发生错误。推荐你的物理文件使用独占方式创建。

如果使用INVALID_HANDLE_VALUE,也需要设置需要申请的内存空间的大小,无论物理文件句柄参数是否有效,这样CreateFileMapping就可以创建一个和物理文件大小无关的内存空间给你,甚至超过实际文件大小,如果你的物理文件有效,而大小参数为0,则返回给你的是一个和物理文件大小一样的内存空间地址范围。返回给你的文件映射地址空间是可以通过复制,集成或者命名得到,初始内容为0。

2)保护设置

就是安全设置,不过一般设置NULL就可以了,使用默认的安全配置。 在win2k下如果需要进行限制,这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是,可以考虑进行限制。

3)高位文件大小

32位地址空间,设置为0。

4)共享内存名称

命名可以包含"Global"或者"Local"前缀在全局或者会话名空间初级文件映射。其他部分可以包含任何除了()以外的字符,可以参考KernelObject Name Spaces。

5)调用CreateFileMapping的时候GetLastError的对应错误

ERROR_FILE_INVALID如果企图创建一个零长度的文件映射,应有此报

ERROR_INVALID_HANDLE如果发现你的命名内存空间和现有的内存映射,互斥量,信号量,临界区同名就麻烦了

ERROR_ALREADY_EXISTS表示内存空间命名已经存在

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

附录:示例

这个程序包括一个客户端和一个服务端,服务端创建共享内存,客户端打开共享内存,两者通过两个事件互斥访问共享内存,实现一个小功能,就是服务端进程从控制台读入数据发送给客户端进程。

 

服务器端:

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
 
#defineFileMapping_NAME "liuker"
#define  FILESIZE 4096
LPVOIDlpdata = NULL;//指针标识首地址
 
voidmain()
{
    if (lpdata != NULL)
    {
       puts("共享内存存在!\n");
    }
    //创建一个有名字标识的共享内存。
    HANDLE hmap =CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0,FILESIZE, FileMapping_NAME);
    if (hmap == NULL)
    {
       puts("创建内存共享失败!\n");
    }
    else
    {
       //映射文件到指针
       lpdata = MapViewOfFile(hmap,FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
       if (lpdata == NULL)
       {
           printf("服务器映射失败!\n");
       }
       else
       {
           int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8,9, 10 };
           memcpy(lpdata, a, 40);
       }
    }
    system("pause");
    UnmapViewOfFile(lpdata);//解除
    CloseHandle(hmap);
    system("pause");
}


 

客户端:

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
 
#defineFileMapping_NAME "liuker"
 
voidmain()
{
    //打开一个指定的文件映射对象,获得共享内存对象的句柄
    HANDLE hmapfile =OpenFileMappingA(FILE_MAP_READ, FALSE, FileMapping_NAME);
    if (hmapfile == NULL)
    {
       printf("打开文件映射对象失败!\n");
    }
    else
    {
       //将一个文件映射对象映射到当前应用程序的地址空间。
       LPVOID lpbase = MapViewOfFile(hmapfile,FILE_MAP_READ, 0, 0, 0);
       if (lpbase == NULL)
       {
           printf("映射失败!\n");
       }
       else
       {
           int *p = lpbase;
           for (int i = 0; i < 10; i++)
           {
              printf("%d\n", p[i]);
           }
       }
       UnmapViewOfFile(lpbase);//解除映射
       CloseHandle(hmapfile);//一个指定的文件映射对象
       system("pause");
    }
}



附录:函数说明

1、CreateFileMapping函数:

HANDLECreateFileMapping(

  HANDLE hFile,

  LPSECURITY_ATTRIBUTESlpFileMappingAttributes,

  DWORD flProtect,

  DWORD dwMaximumSizeHigh,

  DWORD dwMaximumSizeLow,

  LPCTSTR lpName

);

  参数

(1)hFile

映射文件的句柄,若设为0xFFFFFFFF(即:INVALID_HANDLE_VALUE)则创建一个进程间共享的对象

(2)lpFileMappingAttributes

SECURITY_ATTRIBUTES,它指明返回的句柄是否可以被子进程所继承,指定一个安全对象,在创建文件映射时使用。如果为NULL(用ByVal As Long传递零),表示使用默认安全对象。

(3)flProtect

保护文件视图。

a.PAGE_READONLY以只读方式打开映射

b.PAGE_READWRITE以可读、可写方式打开映射

c.PAGE_WRITECOPY为写操作留下备份

可组合使用下述一个或多个常数:

a.SEC_COMMIT为文件映射一个小节中的所有页分配内存

b.SEC_IMAGE文件是个可执行文件

c.SEC_RESERVE为没有分配实际内存的一个小节保留虚拟内存空间

(4)dwMaximumSizeHigh

文件映射的最大长度的高32位。

(5)dwMaximumSizeLow

文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。

(6)lpName

映射文件名,即共享内存的名称。

  返回值

返回的句柄,如果函数是成功的文件映射对象;否则,返回NULL。为了获取更多的错误信息,调用GetLastError。

如果对象存在的函数调用之前,该函数返回的句柄,其目前的大小,而不是指定的大小现有的对象,GetLastError返回ERROR_ALREADY_EXISTS。

如果dwMaximumSizeLow设置为零,GetLastError返回ERROR_INVALID_PARAMETER。

 

2MapViewOfFile函数:

LPVOIDMapViewOfFile(

  HANDLE hFileMappingObject,

  DWORD dwDesiredAccess,

  DWORD dwFileOffsetHigh,

  DWORD dwFileOffsetLow,

  DWORD dwNumberOfBytesToMap

);

  参数

(1)hFileMappingObject

CreateFileMapping函数返回一个文件映射对象的打开的句柄。

(2)dwDesiredAccess

映射对象的文件数据的访问方式,与CreateFileMapping()函数所设置的保护属性相匹配。

a.FILE_MAP_WRITE映射可读可写。文件映射对象必须通过PAGE_READWRITE访问创建。

B.FILE_MAP_READ映射只读。文件映射对象必须通过PAGE_READ 或 PAGE_READWRITE访问创建。

C.FILE_MAP_ALL_ACCESS与FILE_MAP_WRITE相同。

d.FILE_MAP_COPY映射时保留写操作的副本。文件映射对象必须用PAGE_WRITECOPY访问在win95下创建。

(3)dwFileOffsetHigh

文件中映射起点的高32位地址

(4)dwFileOffsetLow

文件中映射起点的低32位地址

(5)dwNumberOfBytesToMap

文件中要映射的字节数。用零映射整个文件映射对象。

  返回值

映射视图的起始地址表示成功;否则,返回NULL。

 

3OpenFileMapping函数:

HANDLEWINAPI OpenFileMapping(

  _In_ DWORD dwDesiredAccess,

  _In_ BOOL bInheritHandle,

  _In_ LPCTSTR lpName

);

  参数

(1)dwDesiredAccess

访问文件映射对象的安全描述。FILE_MAP_ALL_ACCESS、FILE_MAP_EXECUTE、FILE_MAP_READ、FILE_MAP_WRITE

(2)bInheritHandle

如果这个参数为TRUE,通过CreateProcess函数创建一个进程可以继承句柄;否则,手柄不能被继承。

(3)lpName

文件映射对象的名称。可以有一个"Global\" or "Local\"前缀全局或会话名称空间中明确打开一个对象

  返回值

如果函数成功,返回值是打开的句柄到指定的文件映射对象。

如果函数失败,返回值为NULL。


转载于http://blog.sina.com.cn/s/blog_61ed3bc70102vmlc.html