c++ - cpp public variable



動態鏈接時,共享庫中的全局變量和靜態變量會發生什麼變化? (1)

這是Windows和類Unix系統之間的一個非常有名的區別。

無論:

  • 每個進程都有自己的地址空間,這意味著進程之間永遠不會共享任何內存(除非使用某些進程間通信庫或擴展)。
  • 一個定義規則 (ODR)仍然適用,這意味著在鏈接時只能有一個全局變量的定義可見(靜態或動態鏈接)。

所以,這里關鍵的問題是真正的知名度

在所有情況下, static全局變量(或函數)從模塊外(dll / so或者可執行文件)永遠不可見。 C ++標準要求它們具有內部鏈接,這意味著它們在定義它們的翻譯單元(它成為目標文件)之外是不可見的。 所以,這解決了這個問題。

當它有復雜的extern全局變量時。 在這裡,Windows和類Unix系統完全不同。

對於Windows(.exe和.dll), extern全局變量不是導出符號的一部分。 換句話說,不同的模塊根本不知道其他模塊中定義的全局變量。 這意味著如果你嘗試創建一個應該使用DLL中定義的extern變量的可執行文件,你將會得到鏈接錯誤,因為這是不允許的。 您需要提供一個帶有該extern變量定義的對象文件(或靜態庫),並將其與可執行文件和DLL靜態鏈接,從而生成兩個不同的全局變量(一個屬於可執行文件,一個屬於DLL )。

要在Windows中實際導出全局變量,必須使用類似於函數export / import語法的語法,即:

#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif

MY_DLL_EXPORT int my_global;

當你這樣做時,全局變量被添加到導出符號列表中,並可像所有其他函數一樣鏈接。

在類Unix環境(如Linux)中,動態庫稱為“共享對象”,擴展名為.so導出所有extern全局變量(或函數)。 在這種情況下,如果您從任何地方加載時間鏈接到共享對象文件,那麼全局變量將被共享,即作為一個整體鏈接在一起。 基本上,類Unix系統的設計使其與靜態或動態庫鏈接幾乎沒有區別。 同樣,ODR全面應用:一個extern全局變量將在模塊間共享,這意味著它應該只有一個跨所有模塊加載的定義。

最後,在這兩種情況下,對於Windows或類Unix系統,您可以執行動態庫的運行時鏈接,即使用LoadLibrary() / GetProcAddress() / FreeLibrary()dlopen() / dlsym() / dlclose() 。 在這種情況下,您必須手動獲取您希望使用的每個符號的指針,其中包含您希望使用的全局變量。 對於全局變量,只要全局變量是導出符號列表的一部分(通過前幾段的規則dlsym()就可以像使用函數一樣使用GetProcAddress()dlsym() )。

當然,作為一個必要的最後說明: 應該避免全局變量 。 我相信你所引用的文本(關於事物“不清楚”)完全是指我剛剛解釋的特定於平台的差異(動態庫並非真正由C ++標准定義,這是特定於平台的領域,意味著它更不可靠/便攜)。

我試圖了解當全局變量和靜態變量的模塊動態鏈接到應用程序時會發生什麼。 通過模塊,我指的是解決方案中的每個項目(我在Visual Studio中工作過很多!)。 這些模塊內置於* .lib或* .dll或* .exe本身。

我知道應用程序的二進製文件包含數據段中所有單個翻譯單元(目標文件)的全局和靜態數據(如果是const,則為只讀數據段)。

  • 當這個應用程序使用帶加載時動態鏈接的模塊A時會發生什麼? 我假設DLL有一個全局和靜態的部分。 操作系統是否加載它們? 如果是這樣,他們在哪裡裝載?

  • 當應用程序使用帶有運行時動態鏈接的模塊B時會發生什麼?

  • 如果我的應用程序中有兩個模塊都使用A和B,那麼是如下所述創建的A和B全局變量的副本(如果它們是不同的進程)?

  • DLL A和B是否可以訪問應用程序全局變量?

(請說明你的理由)

MSDN引用:

在DLL源代碼文件中聲明為全局的變量被編譯器和鏈接器視為全局變量,但加載給定DLL的每個進程都會獲得其DLL的全局變量的副本。 靜態變量的範圍僅限於聲明靜態變量的塊。 因此,默認情況下,每個進程都有自己的DLL全局和靜態變量實例。

並從here

在動態鏈接模塊時,可能不清楚不同的庫是否有自己的全局實例或全局變量是否共享。

謝謝。





dynamic-linking