发信人: skyice()
整理人: skyice(2000-06-22 00:43:08), 站内信件
|
在使用基指针的进程间共享指针
包含绝对指针的数据不能简单地在 Win32 进程间共享。绝对指针表示
在 Win32 进程的 4GB 地址空间内的绝对内存地址。因为在连接数据结构
时,指针非常有用,也非常有效,所以 Visual C++ 编译器支持一个叫做
基指针的特性,它允许你创建相对指针。基指针用很少几个位的隐藏指针算
法为代价,提供了满足 Wir32 共享内存约束条件的指针的所有优势。
在 Windows NT 上,当在 Win32 进程间共享内存时,绝对指针只会带
来问题。在 Win32s 下,所有进程在一个通用地址空间运行,绝对指针不会
产生问题。在 Windows 98 上,正如我们讨论过的,在 2GB 和 3GB 之间
的通用共享内存有助于简化两种内存共享,一种是关于操作系统的,使它运
行得更快,耗用更少的资源,另一种是关于 Win32 进程的,它们都在这个
地址范围内共享一个通用的对象视图。在 Windows NT 上,所有 Win32 进
程都有一个 0 到 2GB 的私有地址空间。尽管把内存映射到不同地址空间中
的相同地址是可能的,但不能保证一定会成功。因此,如果你想让一个Win32
程序在 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");
需要在共享内存块中保存指针时,基指针就变得很有用。你不必使用相
对指针访问共享内存本身,对于这种情况,绝对指针很好用。如果你要对处
理器周期计数(随着处理器越来越快,这已经越来越不重要了),绝对指针
比基指针稍快一点。但当需要在共享内存块内部存储实际指针时,就应当使
用基指针。它使不同进程可以自由地使用这个指针,而不必担心共享块是否
在不同进程的地址空间中的相同地址上。
静态分配共享页
微软 Visual C++编译器提供了不同进程间共享全局变量的能力。这个
特性依赖编译器和链接器在可执行文件内分配共享页。运行时,加载器允许
这样的页同时映射到几个进程的地址空间。同样,使用动态分配的共享页时,
同一组物理页神奇地映射到多个进程的地址空间。
要使用共享全局变量,必须满足三个要求。第一,全局变量必须初始化。
第二,共享的全局变量应当在在它们自己的段中声明。第三,必须为可执行
文件包含共享变量的段赋共享属性。这通过在模块定义(.DEF)文件中加入
一个项目而实现。让我们一个一个地看一看这些要求。
之所以共享全局变量必须初始化,是因为编译器、链接器和加载器对未
初始化的数据的处理与已初始化的数据的处理不同。已初始化的数据副本实
际出现在可执行文件中。另一方面,未初始化的数据不需要占用可执行文件
的磁盘空间,所以加载器不必从可执行文件中读数据,就可以为未初始化的
数据分配内存。而已初始化的数据的好处是它是以一个已知状态开始其“生
命”的,所以任何进程都不必为之费心。
第二个要求是,共享全局变量应该声明在它们自己的段中,但这不是严
格必须的。例如,一个可执行文件可以共享自己的所有全局变量。Win16 动
态链接库只有一个数据段,可以被所有进程共享。但是这种做法带来的问题
比解决的问题还要多,所以你可能想既有共享全局变量,又有私有全局变量。
要把全局变量声明在自己的段中,可以在一对 data__seg 编译指示语
句之间插入变量声明。你可能已经猜到,这个名称中的 seg 部分来自 16
位 MS-DOS 和 Windows 的分段寻址。下面的例子把一个字符数组放在一个
名为“.shared”的段中:
// Put shared data into section named ".shared"
#pragma data__seg(".shared")
char achPublic[BUFSIZE] = "";
// Set name of data section back to default DATA
#pragma data__seg()
#pragma 关键字看上去好象是一个预处理语句,但实际上并不是。它是
一条指令,编译器用它来提供一些特殊服务。在这种情况下,data__seg 关
键字要求改变存储数据的段的名称。没有指定名称时,就象第二个 #pragma
data__seg 语句那样,就使用默认名称 __DATA。我们为共享内存段起的名
称并不能使它成为共享的,所以我们可以随便起什么名称,比如“DanielB-
oone”。要实现共享,还必须在模块定义文件中把一个段标记为共享的,这
是第三步。
在进程间共享全局变量的第三步是把包含共享变量的段标记为共享的。
要这样做,则在模块定义文件中加入一个条目。这个文件实际上包含着一组
链接器开关。例如,当建立动态链接库时,外部可用的若干函数要列出在
EXPORTS 关键字下。要为可执行文件的段设置内存属性,必须使用SECTIONS
关键字。下面的例子来自一个包含共享全局变量的动态链接库的完整的模块
定义文件。在 SECTIONS 关键字下的条目使名为“.shared”的段可以在不
同进程间共享。
LIBRARY
DESCRIPTION 'Sample Win32 DLL'
SECTIONS
.shared READ WRITE SHARED
EXPORTS
GetPrivateData
SetPrivateData
GetPublicData
SetPublicData
-- 独人独剑独马
浪迹天涯 ...
※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.99.91.11]
|
|