共享进程和DLL之间的全局/静态variables

我想共享一个静态/全局variables只有进程和进程调用的DLL之间。 该exe和dll在相同的内存地址空间。 我不希望variables在其他进程之间共享。


详细阐述问题:

假定a.cpp中有一个静态/全局variablesx 。 exe foo.exe和dll bar.dll都有a.cpp ,因此variablesx在两个图像中。

现在, foo.exedynamic加载(或静态) bar.dll 。 然后,问题是variablesx是否被exe和dll共享,或者不是。

在Windows中,这两个人永远不会共享x :这个exe和dll将会有一个单独的x副本。 但是,在Linux中,exe和dll共享variablesx

不幸的是,我想要Linux的行为。 我首先考虑在Windows上使用pragma data_seg 。 但是,即使我正确设置共享数据段, foo.exebar.dll从不共享x 。 回想一下, bar.dll被加载到bar.dll的地址空间中。 但是,如果我运行foo.exe另一个实例,则共享x 。 但是,我不希望x被不同的进程共享。 所以,使用data_seg失败了。

我可能会使用一个内存映射文件通过在exe和dll之间创build一个唯一的名称,我现在正在尝试。


两个问题:

  1. 为什么Linux和Windows的行为有所不同? 任何人都可以解释更多的这个?
  2. 在Windows上解决这个问题最简单的方法是什么?

首先,我发现这篇文章是对dynamic链接库的一个非常有趣和简洁的阅读(本文只针对Linux,但是这些概念当然也适用于Windows,并且您可能会了解到您所处的不同行为看到)。 特别是静态和dynamic加载的根本区别。

我认为你想要或想要实现的是一个“跨模块单身”模式。 如果你阅读这个主题的答案,我不知道我怎么可能回答你的问题比本·福伊特回答那个职位。 我已经使用他描述的方法在实际上几次(实际上)实现了一个跨模块的单身人士,它像一个魅力。

当然,你将无法保留在cpp文件中只有全局variables坐在那里的清洁。 你将不得不使用一个静态指针和一些访问函数和引用计数。 但它可以工作。 我不太确定如何能够避免foo.exe和foo.exe共享同一个全局数据实例barbar,我从来没有这样做,不能真的想办法做对不起。

为了获得主程序和dll共享x的linux的行为,可以从dll或主程序中导出该variables。 其他模块必须导入该variables。

您可以通过使用DEF文件( 请参阅Microsoft的文档 )或通过使用__declspec(dllexport)标记variables的用途,以及使用其他模块( 请参阅Microsoft的文档 )中的__declspec(dllimport)来执行此操作。 这与在Windows中的模块之间如何共享任何函数,对象或variables是一样的。

如果你想要一个程序在运行时加载一个库,但是主程序可能需要在加载库之前使用这个variables,那么程序应该导出这个variables,并且这个dll应该导入它。 这里有一点鸡鸡蛋的问题,因为DLL依赖于主程序,主程序依赖于DLL。 见http://www.lurklurk.org/linkers/linkers.html#wincircular

我已经写了一个例子,说明如何使用Microsoft的编译器和mingw(gcc在windows中),包括程序和库之间可以链接的所有不同方式(静态地,程序启动时加载dll,dll加载在运行期间)

main.h

 #ifndef MAIN_H #define MAIN_H // something that includes this // would #include "linkage_importing.h" // or #include "linkage_exporting.h" // as appropriate #ifndef EXPLICIT_MAIN LINKAGE int x; #endif // EXPLICIT_MAIN #endif // MAIN_H 

main.c中

 #ifdef EXPLICIT_DLL #include "dyn_link.h" #endif // EXPLICIT_DLL #include <stdio.h> #include "linkage_exporting.h" #include "main.h" #include "linkage_importing.h" #include "dll.h" FNCALL_DLL get_call_dll(void); int main(int argc, char* argv[]) { FNCALL_DLL fncall_dll; fncall_dll = get_call_dll(); if (fncall_dll) { x = 42; printf("Address of x as seen from main() in main.c: %p\n", &x); printf("x is set to %i in main()\n", x); fncall_dll(); // could also be called as (*fncall_dll)(); // if you want to be explicit that fncall_dll is a function pointer printf("Value of x as seen from main() after call to call_dll(): %i\n", x); } return 0; } FNCALL_DLL get_call_dll(void) { #ifdef EXPLICIT_DLL return get_ptr("dll.dll", "call_dll"); #else return call_dll; #endif // EXPLICIT_DLL } 

dll.h

 #ifndef DLL_H #define DLL_H // something that includes this // would #include "linkage_importing.h" // or #include "linkage_exporting.h" // as appropriate // declaration of type to hold a // pointer to the function typedef void(*FNCALL_DLL)(void); #ifndef EXPLICIT_DLL LINKAGE void call_dll(void); #endif // EXPLICIT_DLL #endif // DLL_H 

dll.c

 #ifdef EXPLICIT_MAIN #include "dyn_link.h" #endif // EXPLICIT_MAIN #include <stdio.h> #include "linkage_importing.h" #include "main.h" #include "linkage_exporting.h" #include "dll.h" int* get_x_ptr(void); LINKAGE void call_dll(void) { int* x_ptr; x_ptr = get_x_ptr(); if (x_ptr) { printf("Address of x as seen from call_dll() in dll.c: %p\n", x_ptr); printf("Value of x as seen in call_dll: %i()\n", *x_ptr); *x_ptr = 31415; printf("x is set to %i in call_dll()\n", *x_ptr); } } int* get_x_ptr(void) { #ifdef EXPLICIT_MAIN return get_ptr("main.exe", "x"); // see note in dyn_link.c about using the main program as a library #else return &x; #endif //EXPLICIT_MAIN } 

dyn_link.h

 #ifndef DYN_LINK_H #define DYN_LINK_H // even though this function is used by both, we link it // into both main.exe and dll.dll as necessary. // It's not shared in a dll, because it helps us load dlls :) void* get_ptr(const char* library, const char* object); #endif // DYN_LINK_H 

dyn_link.c

 #include "dyn_link.h" #include <windows.h> #include <stdio.h> void* get_ptr(const char* library, const char* object) { HINSTANCE hdll; FARPROC ptr; hdll = 0; ptr = 0; hdll = LoadLibrary(library); // in a better dynamic linking library, there would be a // function that would call FreeLibrary(hdll) to cleanup // // in the case where you want to load an object in the main // program, you can use // hdll = GetModuleHandle(NULL); // because there's no need to call LoadLibrary on the // executable if you can get its handle by some other means. if (hdll) { printf("Loaded library %s\n", library); ptr = GetProcAddress(hdll, object); if (ptr) { printf("Found %s in %s\n", object, library); } else { printf("Could not find %s in %s\n", object, library); } } else { printf("Could not load library %s\n", library); } return ptr; } 

linkage_importing.h

 // sets up some macros to handle when to use "__declspec(dllexport)", // "__declspec(dllimport)", "extern", or nothing. // when using the LINKAGE macro (or including a header that does): // use "#include <linkage_importing.h>" to make the LINKAGE macro // do the right thing for importing (when using functions, // variables, etc...) // // use "#include <linkage_exporting.h>" to make the LINKAGE macro // do the right thing for exporting (when declaring functions, // variables, etc). // // You can include either file at any time to change the meaning of // LINKAGE. // if you declare NO_DLL these macros do not use __declspec(...), only // "extern" as appropriate #ifdef LINKAGE #undef LINKAGE #endif #ifdef NO_DLL #define LINKAGE extern #else #define LINKAGE extern __declspec(dllimport) #endif 

linkage_exporting.h

 // See linkage_importing.h to learn how this is used #ifdef LINKAGE #undef LINKAGE #endif #ifdef NO_DLL #define LINKAGE #else #define LINKAGE __declspec(dllexport) #endif 

build立明确的both.sh

 #! /bin/bash echo Building configuration where both main echo and dll link explicitly to each other rm -rf mingw_explicit_both mkdir -p mingw_explicit_both/obj cd mingw_explicit_both/obj # compile the source code (dll created with position independent code) gcc -c -fPIC -DEXPLICIT_MAIN ../../dll.c gcc -c -DEXPLICIT_DLL ../../main.c gcc -c ../../dyn_link.c #create the dll from its object code the normal way gcc -shared -odll.dll dll.o dyn_link.o -Wl,--out-implib,libdll.a # create the executable gcc -o main.exe main.o dyn_link.o mv dll.dll .. mv main.exe .. cd .. 

build立明确的dll.sh

 #! /bin/bash echo Building configuration where main explicitly echo links to dll, but dll implicitly links to main rm -rf mingw_explicit_dll mkdir -p mingw_explicit_dll/obj cd mingw_explicit_dll/obj # compile the source code (dll created with position independent code) gcc -c -fPIC ../../dll.c gcc -c -DEXPLICIT_DLL ../../main.c gcc -c ../../dyn_link.c # normally when linking a dll, you just use gcc # to create the dll and its linking library (--out-implib...) # But, this dll needs to import from main, and main's linking library doesn't exist yet # so we create the linking library for main.o # make sure that linking library knows to look for symbols in main.exe (the default would be a.out) gcc -omain.exe -shared main.o -Wl,--out-implib,main.a #note this reports failure, but it's only a failure to create main.exe, not a failure to create main.a #create the dll from its object code the normal way (dll needs to know about main's exports) gcc -shared -odll.dll dll.o dyn_link.o main.a -Wl,--out-implib,libdll.a # create the executable gcc -o main.exe main.o dyn_link.o mv dll.dll .. mv main.exe .. cd .. 

build立明确的main.sh

 #! /bin/bash echo Building configuration where dll explicitly echo links to main, but main implicitly links to dll rm -rf mingw_explicit_main mkdir -p mingw_explicit_main/obj cd mingw_explicit_main/obj # compile the source code (dll created with position independent code) gcc -c -fPIC -DEXPLICIT_MAIN ../../dll.c gcc -c ../../main.c gcc -c ../../dyn_link.c # since the dll will link dynamically and explicitly with main, there is no need # to create a linking library for main, and the dll can be built the regular way gcc -shared -odll.dll dll.o dyn_link.o -Wl,--out-implib,libdll.a # create the executable (main still links with dll implicitly) gcc -o main.exe main.o -L. -ldll mv dll.dll .. mv main.exe .. cd .. 

build立mingw implicit.sh

 #! /bin/bash echo Building configuration where main and echo dll implicitly link to each other rm -rf mingw_implicit mkdir -p mingw_implicit/obj cd mingw_implicit/obj # compile the source code (dll created with position independent code) gcc -c -fPIC ../../dll.c gcc -c ../../main.c # normally when linking a dll, you just use gcc # to create the dll and its linking library (--out-implib...) # But, this dll needs to import from main, and main's linking library doesn't exist yet # so we create the linking library for main.o # make sure that linking library knows to look for symbols in main.exe (the default would be a.out) gcc -omain.exe -shared main.o -Wl,--out-implib,main.a #note this reports failure, but it's only a failure to create main.exe, not a failure to create main.a # create the dll from its object code the normal way (dll needs to know about main's exports) gcc -shared -odll.dll dll.o main.a -Wl,--out-implib,libdll.a # create the executable (exe needs to know about dll's exports) gcc -o main.exe main.o -L. -ldll mv dll.dll .. mv main.exe .. cd .. 

build立mingw static.sh

 #! /bin/bash echo Building configuration where main and dll echo statically link to each other rm -rf mingw_static mkdir -p mingw_static/obj cd mingw_static/obj # compile the source code gcc -c -DNO_DLL ../../dll.c gcc -c -DNO_DLL ../../main.c # create the static library ar -rcs dll.a dll.o # link the executable gcc -o main.exe main.o dll.a mv main.exe ../ cd .. 

构buildmsvc显式both.bat

 @echo off echo Building configuration where both main echo and dll link explicitly to each other rd /s /q win_explicit_both md win_explicit_both\obj cd win_explicit_both\obj rem compile the source code cl /nologo /c /DEXPLICIT_MAIN ..\..\dll.c cl /nologo /c /DEXPLICIT_DLL ..\..\main.c cl /nologo /c ..\..\dyn_link.c rem create the dll from its object code the normal way link /nologo /dll dll.obj dyn_link.obj rem create the executable link /nologo main.obj dyn_link.obj move dll.dll ..\ move main.exe ..\ cd .. 

构buildmsvc显式的dll.bat

 @echo off echo Building configuration where main explicitly echo links to dll, but dll implicitly links to main rd /s /q win_explicit_dll md win_explicit_dll\obj cd win_explicit_dll\obj rem compile the source code cl /nologo /c ..\..\dll.c cl /nologo /c /DEXPLICIT_DLL ..\..\main.c cl /nologo /c ..\..\dyn_link.c rem normally when linking a dll, you just use the link command rem that creates the dll and its linking library. rem But, this dll needs to import from main, and main's linking library doesn't exist yet rem so we create the linking library for main.obj rem make sure that linking library knows to look for symbols in main.exe (the default would be main.dll) lib /nologo /def /name:main.exe main.obj rem create the dll from its object code the normal way (dll needs to know about main's exports) link /nologo /dll dll.obj main.lib rem create the executable link /nologo main.obj dyn_link.obj move dll.dll ..\ move main.exe ..\ cd .. 

构buildmsvc显式main.bat

 @echo off echo Building configuration where dll explicitly echo links to main, but main implicitly links to dll rd /s /q win_explicit_main md win_explicit_main\obj cd win_explicit_main\obj rem compile the source code cl /nologo /c /DEXPLICIT_MAIN ..\..\dll.c cl /nologo /c ..\..\main.c cl /nologo /c ..\..\dyn_link.c rem since the dll will link dynamically and explicitly with main, there is no need rem to create a linking library for main, and the dll can be built the regular way link /nologo /dll dll.obj dyn_link.obj rem create the executable (main still links with dll implicitly) link /nologo main.obj dll.lib move dll.dll ..\ move main.exe ..\ cd .. 

build立msvc implicit.bat

 @echo off echo Building configuration where main and echo dll implicitly link to each other rd /s /q win_implicit md win_implicit\obj cd win_implicit\obj rem compile the source code cl /nologo /c ..\..\dll.c cl /nologo /c ..\..\main.c rem normally when linking a dll, you just use the link command rem that creates the dll and its linking library. rem But, this dll needs to import from main, and main's linking library doesn't exist yet rem so we create the linking library for main.obj rem make sure that linking library knows to look for symbols in main.exe (the default would be main.dll) lib /nologo /def /name:main.exe main.obj rem create the dll from its object code the normal way (dll needs to know about main's exports) link /nologo /dll dll.obj main.lib rem create the executable (exe needs to know about dll's exports) link /nologo main.obj dll.lib move dll.dll ..\ move main.exe ..\ cd .. 

构buildmsvc static.bat

 @echo off echo Building configuration where main and dll echo statically link to each other rd /s /q win_static md win_static\obj cd win_static\obj rem compile the source code cl /nologo /DNO_DLL /c ..\..\dll.c cl /nologo /DNO_DLL /c ..\..\main.c rem create the static library lib /nologo dll.obj rem link the executable link /nologo main.obj dll.lib move main.exe ..\ cd .. 

如果foo.exe总是加载bar.dll,那么你可以在bar.dll中实现该variables并将其导出。 例如,某些文件b.cpp只能编译到bar.dll中,而不能编译到foo.exe中:

 __declspec(dllexport) int x; 

然后将其导入到源文件c.cpp中,编译成foo.exe:

 __declspec(dllimport) int x; 

但是,如果有时foo.exe不加载bar.dll那么这将无法正常工作。 另外,我从记忆中写出这个,所以可能会有一些语法上的错误,但是希望这足以让你指向正确的方向。

我无法回答为什么它是不同的Linux。

我发现这是一个非常有趣的问题,我花时间写了一篇关于如何使用DLL在多个DLL(隐式或显式链接)之间共享数据的广泛教程,同时也确保数据不在单独的进程间共享相同的可执行文件。

你可以在这里find完整的文章:http: //3dgep.com/?p=1759


解决这个问题,我发现工作得相当好是创build一个“共同”或“共享”的DLL,定义所有的数据和方法,你想跨多个DLL共享(但不共享进程)。

假设您想要定义一个可以从主应用程序代码(EXE)访问的单例类,但是您还想访问共享(隐式或显式链接的DLL)中的单例实例。 首先,你需要在“common”DLL中声明单例类:

 // Export the class when compiling the DLL, // otherwise import the class when using the DLL. class __declspec(dllexport) MySingleton { public: static MySingleton& Instance(); }; 

编译CommonDLL项目时,必须通过用__declspec(dllexport)装饰类来导出类声明,并且在使用DLL时(例如在应用程序中),需要通过使用__declspec(dllimport)装饰类来导入类定义__declspec(dllimport)

通过用__declspec(dllexport)说明符装饰类来导出类时,所有类的方法和数据(即使是私有数据)都将从DLL中导出,并可由隐式链接到常见DLL的任何DLL或EXE使用。

MySingleton类的定义可能如下所示:

 MySingleton& MySingleton::Instance() { static MySingleton instance; return instance; } 

编译通用的dll时,会生成两个文件:

  1. Common.DLL文件是共享库,它定义了DLL使用的方法和数据。
  2. Common.LIB文件,用于为从DLL中导出的方法和成员声明存根。

如果您将应用程序与导出的LIB文件链接起来,则DLL文件将在运行时隐式链接(只要在DLLsearchpath中findDLL文件),您就可以访问在CommonDLL.DLL中定义的单例文件。

另外,任何与CommonDLL.LIB文件链接的共享库(例如插件)在应用程序dynamic加载时都可以访问相同的单例实例。

有关此解决scheme(包括源代码示例)的完整说明,请参阅以下我标题为“使用dynamic链接库(DLL)创build插件”的文章:

http://3dgep.com/?p=1759

GCC和Visual Studio之间的区别在于,在Linux上,它隐含地允许代码从其他dynamic链接(共享)库中查看符号,而程序员不需要做任何特殊的事情。 所有符号都可以在共享(dynamic链接)库中供dynamic链接程序在程序运行时parsing。 在Windows上,您必须从DLL中专门导出符号,并将其显式导入到正在使用它的程序或库中。 (通常这是通过一个macros(#define)来完成的,该macros在构builddll本身时扩展为在头文件中具有dllexport声明,但是当某个其他程序使用dll包含头文件时,扩展为具有dllimport在我看来这是一个痛苦的脖子,海湾合作委员会的行为更容易,因为你不必做任何特殊的事情来获得你想要的行为。

在较新版本的GCC上,如果需要,可以在构builddynamic(共享)库时将默认设置设置为隐藏符号。

感谢您提供各种解决scheme。 我已经看了这些选项,并决定使用共享内存来实现交叉模块单身人士,它也适合我。 我已经使用Qt QSharedMemory来实现我的任务,但我使用Win32的CreateFileMapping等写的原型

如果我正确理解你的问题,你是静态链接a.cpp到foo.exe和bar.dll,所以你得到2个x的实例。

如果你创build了第三个dll(比如a.dll),并且你将foo.exe和bar.dlldynamic地链接到a.dll,你会得到你想要的行为:

  1. foo.exe加载a.dll并创buildx。
  2. bar.dll得到加载,并看到a.dll加载,不会再次加载,他们共享x。
  3. 另一个进程加载a.dll,它会得到它自己的x。

我已经看到了这个问题的许多答案,既然有点棘手,不清楚,我想提出以下的情况。 我们想在一个DLL和一个主程序之间共享一个全局variables,并允许从DLL中的不同模块和主程序中访问这个variables。

该variables是一个BOOL指示程序是否应该继续运行或停止。 variables名是ShouldRun ;

在主程序中我们需要把:

 __declspec(dllexport) bool ShouldRun; 

在DLL的主要模块中,我们需要把:

 extern "C" BOOL __declspec(dllexport) ShouldRun = TRUE; 

在DLL项目中的任何其他模块中,我们将使用:

 extern "C" BOOL ShouldRun;