精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>C/C++>>技术精解:内存、进程、线程等>>Win98 内存管理>>Win98 内存管理(12)

主题:Win98 内存管理(12)
发信人: skyice()
整理人: skyice(2000-06-22 00:42:56), 站内信件
动态分配共享页
  内存共享的第二种技术涉及动态分配共享页。尽管Windows 98和Wind-
ows NT 实现不同,但当你使用它们时,一定想得到同样的结果:在两个或
多个进程间访问同一块物理内存页。为在 Windows 98 和 Windows NT 之
间保持完全的兼容性,你应当注意一些实现上的区别。

Windows 98 和 Windows NT 共享的区别
  在 Windows 98 和 Windows NT 上的内存共享的实现之间的关键区别
是关于内存地址的。在 Windows 98 上,一个共享页在所有 Win32 进程看
来位于同一地址,肯定是这样。这是因为共享页被映射到 2GB 到 3GB 之间
共享地址的窗口上。另一方面,在 Windows NT 上,共享页的地址在不同进
程的地址空间中可以不同。它们也可以相同,这取决于哪个对象已经被映射
到进程地址空间。为在两个操作系统中都正确共享页,Win32 进程必须为最
坏的情况(即 Windows NT 共享页的方式)做好准备。
  Windows 98 上诱人的快捷方式在 Windows NT 上将无法使用。例如,
如果你只是在两个进程间共享地址,在 Windows 98上这样总能工作得很好,
而在 Windows NT 上肯定会崩溃。这个快捷方式对正把 Win16 程序从 Wi-
ndows 3.x 移植到 Win32 API,以便在 Windows 98 上进行操作的程序员
来说特别有吸引力。因为 Windows 3.1 上的 Win16 程序可以通过传递指
针而共享数据,快速修改程序使它在 Windows 98 上可以运行的方法是,
再次共享指向共享块的指针。但是不要这么做;应当调用必需的函数,以保
证你能在 Windows NT 和未来的 Win32 实现上运行。
  Windows 98 和 Windows 3.x 共享数据的方式之间存在重要差别。在
Windows 3.x 中,要分配共享对象,程序员调用 GlobalAlloc() 并传人 
GMEM_SHARE(或 GMEM_DDESHARE)标志。该标志将取代一些 windows 3.x
对动态分配的段强行执行的自动内存清理工作。在 Win32 中,这个标志不
起作用。本章前面部分讲过,所有用 GlobalAlloc() 分配的内存都放在进
程私有内存的地址范围内。这种内存共享机制在 Win32 API 中不支持。

用于共享页的 Win32 函数
  共享内存页时调用的函数和共享内存映射文件时调用的函数相同。首先
调用 CreateFileMapping(),创建一个文件映射对象。调用时,如果传递
的文件句柄参数为(HFILE)-1,则文件映射对象就会把自己连接到一个系
统页文件。要访问具体的共享字节,调用函数 MapViewOfFile()。在前面
一节我们已经讨论过这两个函数。下面的代码片段创建了一个名为“RICK_
DATA”的共享内存对象:
  DWORD dwSize;    // Desired size of file mapping object.
  HANDLE hfm;      // Handle to file mapping object
  LPVOID lpDATA;   // Pointer to shared data

  // Createfile mapping object
  hfm = CreateFileMapping(
    (HANDLE)0xffffffff,  // Handle
    0,                   // Ptr to Security
    PAGE_READWRITE,      // Page protection
    0,                   // File Size-Hi-bits
    dwSize,              // File Size-Low-bits
    "RICK_DATA");        // Mapping Name

  if(hfm == 0)
  {
    AfxMessageBox("Unable to Create File Mapping Object");
    return(FALSE);
  }

  // Exit if named object already exists
  if(GetlastError() == ERROR_ALREADY_EXISTS)
  {
    AfxMessageBox("File Mapping Object Already Exists");
    CloseHandle(hfm);
    return(FALSE);
  }

  // Create view of file mapping object
  lpData = MapViewOfFile(hfm,
    FILE_MAP_WRIOTE,  // Page Protection
    0,                // File Offset-Hi 32-bits
    0,                // File Offset-Low 32-bits
    0);               // Size of view

  if(lpData == 0)
  {
    AfxMessageBox("Can't Create View of Mapping Object");
    CloseHandle(hfm);
    return(FALSE);
  }

  注意,在这个代码片段中,调用 CreateFileMapping()时提供的文件
句柄为 0xffffffff,或 -1。我们也可以同样简单地提供一个 INVALID_
HANDLE_VALUE, 它会使这段代码更容易理解。 共享区域的大小是在调用 
CreateFileMapping() 时设置的,在这个例子中就是 dwSize。这里的假
设是我们限制共享数据不超过 4GB。本例中调用 MapViewOfFile() 时传
入的视图大小为 0,它把视图大小设置为包含了所有共享页的大小。
  创建共享内存区时,你将发现文件映射对象的保护属性限制了可以在
视图对象中设置的属性。例如,可写(FILE_MAP_WRITE)视图不能映射文
件映射对象中的只读(FILE_READONLY)设置。文件映射对象设置了允许
的最大保护,并强迫各视图依照执行。
  前面说过,在 Windows NT 上的给定共享页可以映射到不同进程中的
不同地址。当设计内存共享策略时,必须小心避免依赖于 Windows 98 的
实现,Windows 98 一定会把共享页为不同进程放在同一地址。微软 Vis-
ual C++ 编译器支持基指针的创建,它是编程争论最少的特性,用以简化
对共享数据的访问。

在使用基指针的进程间共享指针
  包含绝对指针的数据不能简单地在 Win32 进程间共享。绝对指针表示
在 Win32 进程的 4GB 地址空间内的绝对内存地址。因为在连接数据结构
时,指针非常有用,也非常有效,所以 Visual C++ 编译器支持一个叫做
基指针的特性,它允许你创建相对指针。基指针用很少几个位的隐藏指针算
法为代价,提供了满足 Wir32 共享内存约束条件的指针的所有优势。
  在 Windows NT上,当在 Win32 进程间共享内存时,绝对指针只会带
来问题。在 Win32s下,所有进程在一个通用地址空间运行,绝对指针不会
产生问题。在 Windows 98 上,正如我们讨论过的,在 2GB 和 3GB 之间
的通用共享内存有助于简化两种内存共享,一种是关于操作系统的,使它运
行得更快,耗用更少的资源,另一种是关于 Win32 进程的,它们都在这个
地址范围内共享一个通用的对象视图。在 Windows NT 上,所有 Win32 进
程都有一个 0 到 2GB 的私有地址空间。尽管把内存映射到不同地址空间中
的相同地址是可能的,但不能保证一定会成功。因此,如果你想让一个 Wi-
n32 程序在 Windows NT上适当地共享内存,你必须假设共享内存在访问该
内存的每个进程中将出现在不同地址处。在共享内存对象中,只有相对指针
可以使用。
  相对指针是一个偏移量,这对曾编写 Win16 程序的程序员来说很熟悉
了,它依赖于 Intel 的分段寻址。虽然你可以使用自己的指针算法,然而
编译器对基指针的支持却使指针算法可以自动完成。微软第一次引人基指针
的支持是为在 16 位程序中更有效地访问分段式内存。在 Win32 下,把这
个同样的特性用于在共享内存中创建和使用相对指针。
  为支持基指针,微软向它的编译器加入了一个新的关键字:___based,
它以一个基地址作为操作数。假设有个基地址 pData(也许是 char *),
下面是如何定义名为 pBasePointer 的基指针:
  char___based(pData) * pBasedPointer;
  声明以后,基指针就可以按普通指针的方式使用:
  char___based(pData) * pBasedPointer;
  char * pData;
  char___based(pData) * pBasedPointer;
  pData = (char *)MapviewOfFile(...);
  pBasedPointer = pData;
  strcpy(pBasedPointer,"Relative(based) potnter");
  Strcpy(pData,"Absolute pointer");
  需要在共享内存块中保存指针时,基指针就变得很有用。你不必使用相
对指针访问共享内存本身,对于这种情况,绝对指针很好用。如果你要对处
理器周期计数(随着处理器越来越快,这已经越来越不重要了),绝对指针
比基指针稍快一点。但当需要在共享内存块内部存储实际指针时,就应当使
用基指针。它使不同进程可以自由地使用这个指针,而不必担心共享块是否
在不同进程的地址空间中的相同地址上。

--
我想我是海,宁静的深海
不是谁都明白 ...

※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.99.88.17]

[关闭][返回]