簡體   English   中英

在進程和DLL之間共享全局/靜態變量

[英]Sharing a global/static variable between a process and DLL

我想在進程和進程調用的dll之間共享靜態/全局變量。 exe和dll位於相同的內存地址空間中。 我不希望變量在其他進程之間共享。


詳述問題:

假設a.cpp中存在靜態/全局變量x exe foo.exe和dll bar.dll都有一個a.cpp ,因此變量x在兩個圖像中。

現在, foo.exe動態加載(或靜態) bar.dll 然后,問題是變量x是否由exe和dll共享。

在Windows中,這兩個人從不共享x :exe和dll將有一個單獨的x副本。 但是,在Linux中,exe和dll共享變量x

不幸的是,我想要Linux的行為。 我首先考慮在Windows上使用pragma data_seg 但是,即使我正確設置了共享數據段, foo.exebar.dll也不會共享x 回想一下, bar.dll被加載到的地址空間foo.exe 但是,如果我運行另一個foo.exe實例,則共享x 但是,我不希望x被不同的進程共享。 因此,使用data_seg失敗了。

我可以通過在exe和dll之間創建一個唯一的名稱來使用內存映射文件,我現在正在嘗試。


兩個問題:

  1. 為什么Linux和Windows的行為不同? 任何人都可以解釋更多嗎?
  2. 在Windows上解決此問題最簡單的方法是什么?

要獲得主程序和dll共享相同x的linux的行為,可以從dll或主程序中導出該變量。 另一個模塊必須導入該變量。

您可以通過使用DEF文件( 請參閱microsoft的文檔 ),或使用帶有__declspec(dllexport)的變量來定義它的用途,並在其使用的任何其他模塊中使用__declspec(dllimport)請參閱microsoft的文檔 )。 這與在Windows中的模塊之間共享任何函數,對象或變量的方式相同。

如果您希望程序在運行時加載庫,但主程序可能必須在加載庫之前使用該變量,程序應該導出變量,並且dll應該導入它。 這里有一點雞和蛋的問題,因為dll取決於主程序,主程序取決於dll。 http://www.lurklurk.org/linkers/linkers.html#wincircular

我已經編寫了一個示例,說明如何使用Microsoft的編譯器和mingw(windows中的gcc)來完成此操作,包括程序和庫可以相互鏈接的所有不同方式(靜態,在程序啟動時加載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

構建mingw顯式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 ..

構建mingw顯式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 ..

構建mingw顯式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 ..

建立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 ..

構建msvc顯式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 ..

構建msvc顯式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 ..

構建msvc顯式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 ..

構建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 ..

構建msvc 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 ..

首先,我發現這篇文章是關於動態鏈接庫的非常有趣和簡潔的閱讀(這篇文章僅針對Linux,但這些概念肯定也適用於Windows,你可能會對你的不同行為有所了解看到)。 特別是靜態和動態加載之間的根本區別。

我認為你想要或正在嘗試實現的是“跨模塊單例”模式。 如果您閱讀了這個帖子的答案,我不知道我怎么能比Ben Voigt回答那個帖子更好地回答你的問題。 我之前(實際上幾次)使用他描述的方法實現了一個跨模塊單例,它就像一個魅力。

當然,你將無法保持只有全局變量坐在cpp文件中的清潔。 您將不得不使用靜態指針和一些訪問器函數和引用計數。 但它可以工作。 我不太確定怎么可能避免foo.exe和foo.exe共享同一個全局數據實例一個bar.dll,我從來沒有這樣做過,也無法真正想辦法對不起

如果foo.exe總是加載bar.dll,那么你可以在bar.dll中實現該變量並將其導出。 例如,某些文件b.cpp只編譯成bar.dll,而不是編譯成foo.exe:

__declspec(dllexport) int x;

然后將其導入到編譯到foo.exe的源文件c.cpp中:

__declspec(dllimport) int x;

但是,如果有時foo.exe沒有加載bar.dll,那么這將無法正常工作。 另外,我是從內存中寫這個,所以可能會有一些語法錯誤,但希望它足以指出你正確的方向。

我無法回答為什么它與Linux不同。

我發現這是一個非常有趣的問題,我花時間編寫了一個關於如何使用DLL在多個DLL之間共享數據(隱式或顯式鏈接)的廣泛教程,同時也確保數據不在不同進程之間共享相同的可執行文件。

您可以在此處找到完整的文章:http: //3dgep.com/?p = 1759


我發現這個問題很有效的解決方案是創建一個“公共”或“共享”的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)修飾類來導出類declaratoin,當您使用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文件將在運行時隱式鏈接(只要在DLL搜索路徑中找到DLL文件),您就可以訪問CommonDLL.DLL中定義的單例。文件。

此外,任何鏈接到CommonDLL.LIB文件的共享庫(例如插件)在應用程序動態加載時都可以訪問相同的單例實例。

有關此解決方案(包括源代碼示例)的完整說明,請查看我發布的以下文章“使用動態鏈接庫(DLL)創建插件”:

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

GCC和Visual Studio之間的區別在於,在Linux上,它隱式允許代碼查看來自其他動態鏈接(共享)庫的符號,而無需程序員必須執行任何特殊操作。 所有符號都可在共享(動態鏈接)庫中使用,以供動態鏈接程序在程序運行時解析。 在Windows上,您必須專門從DLL導出符號,並將其顯式導入到正在使用它的程序或庫中。 (通常這是通過一個宏(#define)來完成的,它在構建dll本身時擴展為在頭文件中包含dllexport聲明,但是當使用dll的其他程序包含頭文件時,它會擴展為具有dllimport相反。在我看來,這是一個痛苦的問題,GCC的行為更容易,因為你不需要做任何特別的事情來獲得你通常想要的行為。

在較新版本的GCC上,如果需要,可以在構建動態(共享)庫時將默認值設置為隱藏符號。

感謝您提供各種解決方案。 我查看了這些選項並決定使用共享內存實現交叉模塊單例,它也適用於我。 我使用Qt QSharedMemory來完成我的任務,但我使用Win32 CreateFileMapping等編寫的原型。

我已經看到了這個問題的很多答案,因為它有點棘手而且不清楚我想帶來以下場景。 我們希望在DLL和主程序之間共享一個全局變量,並允許從DLL和主程序中的不同模塊訪問此變量。

變量是BOOL,指示程序是否應繼續運行或停止。 變量名是ShouldRun ;

在主程序中我們需要:

__declspec(dllexport)  bool ShouldRun;

在DLL的主模塊中我們需要放:

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

在DLL項目中的任何其他模塊中,我們將使用:

extern  "C" BOOL ShouldRun;

如果我正確理解你的問題,你將a.cpp靜態鏈接到foo.exe和bar.dll,所以你得到2個x實例。

如果您創建了第三個dll(例如a.dll),並且您將foo.exe和bar.dll動態鏈接到a.dll,您將獲得所需的行為:

  1. foo.exe加載a.dll並創建x。
  2. bar.dll被加載並看到a.dll已加載並且不再加載它們,它們共享x。
  3. 另一個進程加載a.dll,它獲得自己的x。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM