怎样实现Win 95下内存共享

  本文讨论Win95环境下两个或多个进程之间通过Win32 API实现内存共享的方法。共享内存的方法可以归纳为四种:内存映像文件、共享内存页、动态申请和静态申请、定制资源。内存映像文件是其它内存共享方法的基础,也是本文介绍的重点。同时简单介绍了进程之间对共享数据进行访问的同步。本文的程序采用Visual C++的MFC实现。
  一、共享内存
  Windows 95采用页式内存管理,把进程私有页面和共享页面放在不同的地址空间。私用页放在4MB到2GB之间,当进行从一个Win32进程到另一个进程的上下文转换时,一组私有页被映射出去,另外一组页面被影射到这个地址范围,这是通过更新CPU的页表来实现的。另一方面,把Windows 95共享页面放在2GB到3GB之间。当一个上下文转换发生时,Windows95内存管理者不用操心这段区域的页与页表。由于把共享页放在一边,Windows95的内存管理使得进程之间的内存共享简单快速。
  在Win32上的所有共享内存依赖于Win32的内存映像I/O支持。一个内存映像文件使用共享内存来提供公共的基于文件的数据映像到共享数据的进程。内存映像文件I/O的一个不太引人注意的特点是它支持内存共享。在Win32进程间共享内存,使用了一个像内存映像文件一样的操作系统虚内存页文件。共享页属于系统的页文件,而不属于一个永久的命名的文件。
  本文从内存映像文件开始介绍内存共享,因为它是其它类型共享的基础。然后讨论共享内存页,这里介绍两种共享页类型:动态申请和静态申请。我们将讨论的第四种内存共享是定制资源,这种类型基于只读取,它提供了一种存储和查询大量数据,根据需要从执行文件编页到内存的有效方法。
  本文最后给出一个可以实际运行的例子供程序员参考。例子涉及到内存共享的另一个问题:同步。多个进程可以拥有同一信号灯、事件或互斥等对象的句柄,所以这些对象可用于实现进程之间的同步。例子中采用了互斥方法。
  二、内存映像文件I/O
  内存映像文件I/O是Win32 API处理磁盘文件的一种方式。如果虚拟内存管理不控制数据缓冲和内存缓冲,则当一个文件被映像到内存区,从映像内存读写数据和从文件读写数据将产生同样的效果。内存映像文件I/O效率很高,且使用非常方便。内存映像文件I/O允许两个或多个进程共享基于文件的数据。每个和共享有关的进程直接存取一组公共页。由于共享占用的资源最小,当大量的数据必须被共享时,通过内存映像文件I/O共享就显得非常有用。
  内存映像文件支持需要三个Windows内核对象:文件、文件映像和视图。为了映像一个文件到内存,首先从磁盘打开一个文件,从而建立文件对象,然后将文件对象连接到内存映像文件对象,为了在不同进程间同步数据,它提供了一个基于磁盘文件的逻辑连接。为了访问文件的数据块,还需要一个数据指针,视图对象正是用于此目的。一个内存映像文件对象可以创建多个视图,分别存取文件的不同部分。下图示意了三个对象之间的关系。
  三、内存映像文件的使用和共享
  内存映像文件I/O的使用由以下步骤组成:
  * 打开磁盘文件。这一步可采用如CreateFile()或OpenFile()等函数。CreateFile()功能较强,但OpenFile()参数较少,易于使用。为了打开一个基于磁盘的文件,宜采用OpenFile():
  HFILE WINAPI OpenFile (
  LPCSTR lpFileName,
  LPOFSTRUCT lpOF,
  UINT uStyle)
  注意OpenFile的返回值为-1时表示打开文件失败。参数uStyle定义了打开文件标志(OF_READ、OF_WRITE、OF_READWRITE等),不同标志标明了怎样存取内存;文件被其它进程共享的方式(OF_SHARE_EXCLUTIVE、OF_SHARE_DENY_WRITE、等),及文件打开时采取的动作(OF_CREATE、OF_DELETE、OF_EXIST等)。为了关闭文件,可以调用函数CloseHandle()。
  * 创建文件映像对象。为了使用内存映像文件,第二个步骤是通过函数CreateFileMapping() 创建文件映像对象。这个函数接受上一步得到的文件句柄作为参数。
  HANDLE WINAPI CreateFileMapping (
  HANDLE hFile,
  LPSECURITY _ATTRIBUTES lpsa,
  DWORD dwPROTECT,
  DWORD dwMaxSizeHigh,
  DWORD dwMaxSizeLow,
  LPCSTR lpszMapName)
  hFile是从OpenFile()调用返回的文件句柄;dwPROTECT是页保护标识(PAGE_READONLY等),它必须和文件打开时定义的文件存取标志一致;两个dwMaxSize共同定义了最大文件尺寸;lpszMapName定义了文件映像对象的名字。
  如果你向hFile传送的参数为-1(无效的文件句柄),并不意味着函数调用失败,相反地,这种情况创建共享内存而非文件共享存取,本文后面将讨论这一点。
  lpszMapName是你给文件映像对象起的名字,必须确保其唯一性,因为和一个未知进程的名字冲突会产生非希望的共享。要共享此文件的进程通过相同的文件映像名实现共享,具体做法可以有三种情况:CreateFileMapping()、OpenFileMapping()、DuplicateHandle()。第二个(或以后的)进程调用CreateFileMapping()实际上并没有创建新的对象,而只是与已有对象连接。若采用OpenFileMapping(),需假定文件映像对象已由前面运行的进程用CreateFileMapping()创建。通过 DuplicateHandle()共享的做法需要经过别的途径获得对象句柄。
  * 创建视图对象。经过前面两个步骤后,存取内存映像文件或共享文件的第三个步骤是调用MapViewOfFile()创建视图对象。可以在一个文件中创建多个视图,以便分别访问文件的不同部分。调用MapViewOfFile()时需传递视图在文件的起始位置偏移和要映射的字节数。
  四、共享内存页
  分动态和静态两种情况介绍共享内存页。
  * 动态分配共享页。内存共享的第二种情况是动态申请共享页。这里要用到与共享内存映像文件相同的函数。我们首先用文件句柄参数为-1来调用CreateFileMapping()创建一个文件映像对象,并通过文件尺寸参数设定共享内存区域的大小。文件映像对象将自己连接到系统页文件。然后调用MapViewOfFile()创建内存视图,以确定要访问的特定内存区域,包括起始点和大小。

dollar.gif (7090 bytes)宝藏五:寻找您心灵的另一半