看流星社区

 找回密码
 注册账号
查看: 2512|回复: 0

多个进程范文DLL中的函数/全局变量/数据共享段问题

[复制链接]

该用户从未签到

发表于 2016-9-10 07:23:29 | 显示全部楼层 |阅读模式

第一种方法:数据段共享#pragma data_seg
一、#pragma data_seg()
#pragma data_seg()一般用于DLL中。也就是说,在DLL中定义一个共享的,有名字的数据段。最关键的是:这个数据段中的全局变量可以被多个进程共享。

否则多个进程之间无法共享DLL中的全局变量。

2,共享数据必须初始化,否则微软编译器会把没有初始化的数据放到.BSS段中,从而导致多个进程之间的共享行为失败。

3,你所谓的结果正确是一种错觉。如果你在一个DLL中这么写:

#pragma data_seg("MyData")

int g_Value; // Note that the global is not initialized.

#pragma data_seg()

DLL提供两个接口函数:

int GetValue()
{
     return g_Value;
}

void SetValue(int n)
{
     g_Value = n;
}

然后启动两个进程A和B,A和B都调用了这个DLL,假如A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值不一定是5,而是一个未定义的值。因为DLL中的全局数据对于每一个调用它的进程而言,是私有的,不能共享的。

假如你对g_Value进行了初始化,那么g_Value就一定会被放进MyData段中。换句话说,如果A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值就一定是5!

这就实现了跨进程之间的数据通信!

二、
#pragma data_seg("flag_data")  
   int count=0;  
#pragma data_seg()  
#pragma comment(linker,"/SECTION:flag_data,RWS")  


这种方法只能在没有def文件时使用,如果通过def文件进行导出的话,那么设置还得在def文件内设置了。

SETCTIONS  
flag_data READ WRITE SHARED  


在主文件中,用#pragma data_seg建立一个新的数据段并定义共享数据,其具体格式为:

#pragma data_seg ("shareddata") //名称可以自己定义,但必须与下面的一致。  
  
HWND sharedwnd=NULL;//共享数据  
  
#pragma data_seg()  

仅定义一个数据段还不能达到共享数据的目的,还要告诉编译器该段的属性,有两种方法可以实现该目的(其效果是相同的),
(1)一种方法是在.DEF文件中加入如下语句: SETCTIONS shareddata READ WRITE SHARED
(2)另一种方法是在项目设置链接选项(Project Setting --〉Link)中加入如下语句: /SECTION:shareddata,rws
以上方法会到代码中找shareddata命名的data_seg,如果找不到,那么编译会有警告出现。

1、第一点:什么是共享数据段?为什么要用共享数据段??它有什么用途??
在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;
而在Win32环境中,情况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。

因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

#pragma data_seg预处理指令用于设置共享数据段。例如:

#pragma data_seg("SharedDataName")
HHOOK hHook=NULL; //必须在定义的同时进行初始化!!!!
#pragma data_seg()

在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。
再加上一条指令#pragma comment(linker,"/section:.SharedDataName,rws"),[注意:数据节的名称is case sensitive]那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。

当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。(这项技术又叫code Injection技术,被广泛地应用在了病毒、黑客领域!呵呵^_^)


2、第二点:在具体使用共享数据段时需要注意的一些问题!

Win32 DLLs are mapped into the address space of the calling process. By default, each process using a DLL has its own instance of all the DLLs global and static variables. (注意: 即使是全局变量和静态变量也都不是共享的!) If your DLL needs to share data with other instances of it loaded by other applications, you can use either of the following approaches:

三、
1、背景

已经将类指针通过共享段共享,在另一进程中是能访问此指针的。


//----------------------------------------数据定义-------------------------------------------  
#pragma data_seg("flag_data")  
// 保存Gina类的指针  
Gina *g_pWlxContext = NULL;  
#pragma data_seg()  

2、出现问题

在另一进程中通过此类指针访问成员访问成员函数是成功的,但通过类指针去访问public属性的数据成员,却显示“Memory  UnAccess”


3、解决方法:

3、1  (可行)

将类数据成员单独用全局变量保存起来。


//----------------------------------------数据定义-------------------------------------------  
#pragma data_seg("flag_data")  
// 保存Gina类的指针  
Gina *g_pWlxContext = NULL;  
HANDLE  g_hLsa = NULL;              
#pragma data_seg()  

保存起来:
Gina::Gina(IWinLogon* pWinLogon, HANDLE hLsa)  
  : _pWinLogon(pWinLogon), _hLsa(hLsa), _hToken(0), _profilePath(0), _pStatusWindow(0) {  
      g_hLsa = _hLsa;  

3、2  将整个类作为共享区(可行性未验证)



其他重要内容:
动态链接库 (DLL) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

    可见,动态链接库的主要作用就是共享函数库。操作系统提供这样一种机制,保证函数库在系统中只有一份实例。至于DLL中的全局变量,对于每一个进程调用,操作系统会复制一份副本,以避免不同进程之间的干扰。

    理论上讲,因为DLL的目的主要在于共享函数库,所提供的函数应尽量具有中立性,不应与应用细节关联太紧密。因为副本的增加要多占用系统资源与处理负担,所以,应该尽量避免在DLL中使用过多的全局变量,尤其是具有大量数据成员和成员函数的C++全局类对象。

    DLL是为面向模块和代码复用而设计,但它却时常被用来作软件之间的分层设计。用作分层设计目标的DLL,多进程复用没有太多的意义。实际应用当中也有一种情况,如果一个DLL操控制某个设备在系统中具有唯一性,我们必须要求该DLL考虑不能用于多个进程实例的特性。

    然而仅凭操作系统的一些基础性支持,就能做到实例唯一吗?非也,编程者要仔细理解DLL原理与机制,并作大量的练习处理后,才能做到真正的系统实例唯一。

    方法之一:在DLL中定义全局变量

    DLL定义的全局变量可以被调用进程访问;DLL可以访问调用进程的全局数据。我们可以这样设想,在DLL中定义一标志全局变量,每当进程调用之前,先检查此变量就可以得知是不是已有进程在调用DLL。

    然而,DLL中的全局变量会随新进程的应用而拷贝副本。你在A进程中看到的全局变量Flag_XX与你在B进程中看到的Flag_XX其实不是同一样标记。

    所以简单的定义全局变量作进程标记,是不能实现进程应用唯一性的判断。

    方法二:定义共享数据段

    延续方法一的思路。如果能有一种机制,保证多个进程无论启动多少个DLL实例,全局变量不进行副本复制,这个问题就可以解决了。

    事实上,操作系统也确实有这个能力,编译器也为此类特殊应用作好的准备

还有一句话是否正确?

进程间是相互独立的,其实完全可以看成A、B两个进程各自有一份单独的liba.so和libb.so,相应的动态库的代码段和数据段都是各个进程各自有一份的。
然后在这个基础上,由于代码段是不会被修改的,所以操作系统可以采用copy on write的优化技术,让两个进程共享同一份物理内存。这是属于在不改变系统行为的基础上,为了节省内存,的优化技术。
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-19 09:59

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表