動的にリンクされると、共有ライブラリのグローバル変数と静的変数はどうなりますか?


127

グローバルと静的変数を持つモジュールがアプリケーションに動的にリンクされたときに何が起こるかを理解しようとしています。モジュールとは、ソリューション内の各プロジェクトを意味します(ビジュアルスタジオでよく仕事をしています!)。これらのモジュールは、*。libまたは* .dllまたは* .exe自体に組み込まれています。

アプリケーションのバイナリには、データセグメント内の個々のすべての翻訳単位(オブジェクトファイル)のグローバルデータと静的データ(およびconstの場合は読み取り専用データセグメント)が含まれていることを理解しています。

  • このアプリケーションがロード時の動的リンクを備えたモジュールAを使用するとどうなりますか?DLLにはグローバルとスタティックのセクションがあると思います。オペレーティングシステムはそれらをロードしますか?もしそうなら、彼らはどこにロードされますか?

  • そして、アプリケーションがランタイム動的リンクでモジュールBを使用するとどうなりますか?

  • アプリケーションにAとBの両方を使用する2つのモジュールがある場合、AとBのグローバルのコピーは以下のように作成されますか(それらが異なるプロセスの場合)?

  • DLL AおよびBはアプリケーショングローバルにアクセスできますか?

(理由も記入してください)

MSDNからの引用:

DLLソースコードファイルでグローバルとして宣言された変数は、コンパイラとリンカーによってグローバル変数として扱われますが、特定のDLLをロードする各プロセスは、そのDLLのグローバル変数の独自のコピーを取得します。静的変数のスコープは、静的変数が宣言されているブロックに限定されます。その結果、各プロセスには、デフォルトでDLLグローバル変数と静的変数の独自のインスタンスがあります。

そしてここから:

モジュールを動的にリンクする場合、異なるライブラリが独自のグローバルのインスタンスを持っているかどうか、またはグローバルが共有されているかどうかが不明確になることがあります。

ありがとう。


3
モジュールおそらく意味LIBSを。現在のところ、通常のライブラリとは異なり、モジュールがどうなるかをより正確に定義し、セマンティクスを変えて、モジュールをC ++標準に追加するという提案があります
デビッドロドリゲス-dribeas 2013年

ああ、それを明確にすべきだった。ソリューション内のさまざまなプロジェクト(私はビジュアルスタジオでよく作業します)をモジュールと見なします。これらのモジュールは* .libまたは* .dllに組み込まれています。
Raja

3
@DavidRodríguez-dribeas「モジュール」という用語は、実行可能プログラム、ダイナミックリンクライブラリ(.dll)、共有オブジェクト(.so)などのスタンドアロン(完全にリンクされた)実行可能ファイルの正しい技術用語です。これは完全に適切であり、意味は正しく、よく理解されています。私が説明したように、「モジュール」という名前の標準機能があるまで、その定義は従来のもののままです。
Mikael Persson 2013年

回答:


176

これは、WindowsとUnixライクなシステムのかなり有名な違いです。

何があっても:

  • プロセスには独自のアドレス空間があります。つまり、プロセス間でメモリが共有されることはありません(プロセス間通信ライブラリまたは拡張機能を使用しない限り)。
  • 一つの定義規則(ODR)はまだあなたが唯一のリンク時(静的または動的リンク)で見えるグローバル変数の一つの定義を持つことができることを意味し、適用されます。

したがって、ここでの重要な問題は本当に可視性です。

すべての場合において、staticグローバル変数(または関数)がモジュール(dll / soまたは実行可能ファイル)の外部から見えることはありません。C ++標準では、これらが内部リンケージを持っている必要があります。つまり、これらは、それらが定義されている(オブジェクトファイルになる)変換単位の外部には表示されません。それで、それはその問題を解決します。

複雑になるのは、externグローバル変数がある場合です。ここでは、WindowsとUnixのようなシステムは完全に異なります。

Windows(.exeおよび.dll)の場合、externグローバル変数はエクスポートされたシンボルの一部ではありません。つまり、異なるモジュールは、他のモジュールで定義されたグローバル変数をまったく認識しません。これは、たとえばexternDLLで定義された変数を使用することになっている実行可能ファイルを作成しようとすると、リンカーエラーが発生することを意味します。これは許可されていないためです。オブジェクトファイル(または静的ライブラリ)にそのextern変数の定義を提供し、実行可能ファイルとDLLの両方に静的にリンクする必要があります。その結果、2つの異なるグローバル変数(1つは実行可能ファイルに属し、もう1つは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グローバル変数(または関数)をエクスポートします。この場合、どこからでも共有オブジェクトファイルにロード時リンクを行うと、グローバル変数が共有されます。つまり、1つにリンクされます。基本的に、Unixライクなシステムは、静的ライブラリと動的ライブラリのリンクに実質的に違いがないように設計されています。繰り返しますが、ODRは全面extern的に適用されます。つまり、グローバル変数はモジュール間で共有されます。つまり、ロードされたすべてのモジュールにわたって1つの定義のみを持つ必要があります。

最後に、どちらの場合も、WindowsまたはUnixライクなシステムでは、動的ライブラリのランタイムリンクを実行できます。つまり、LoadLibrary()/ GetProcAddress()/ FreeLibrary()またはdlopen()/ dlsym()/ を使用できますdlclose()。その場合、使用する各シンボルへのポインタを手動で取得する必要があります。これには、使用するグローバル変数が含まれます。グローバル変数の場合は、使用することができますGetProcAddress()dlsym()あなたはグローバル変数は、(前の段落のルールによる)、エクスポートシンボルリストの一部であることは、機能のために行うとちょうど同じ。

そしてもちろん、必要な最後の注記として、グローバル変数は避けるべきです。そして、あなたが引用したテキスト(「不明確」なことについて)は、先ほど説明したプラットフォーム固有の違いを正確に参照していると思います(動的ライブラリはC ++標準では実際には定義されていません。これはプラットフォーム固有の領域であり、つまり信頼性が低い/ポータブルです)。


5
すばらしい回答、ありがとうございます!フォローアップがあります:DLLは自己完結型のコードとデータなので、実行可能ファイルと同様のデータセグメントセクションがありますか?共有ライブラリが使用されているときに、このデータがどこにどのように読み込まれるかを理解しようとしています。
Raja

18
@Rajaはい、DLLにはデータセグメントがあります。実際、ファイル自体、実行可能ファイル、DLLは実質的に同じですが、唯一の実際の違いは、「メイン」関数が含まれていると言うために実行可能ファイルに設定されるフラグです。プロセスがDLLをロードすると、そのデータセグメントがプロセスのアドレス空間のどこかにコピーされ、静的初期化コード(重要でないグローバル変数を初期化する)もプロセスのアドレス空間内で実行されます。ロードは実行可能ファイルの場合と同じですが、プロセスのアドレス空間が新しく作成されるのではなく拡張される点が異なります。
Mikael Persson 2013年

4
クラスのインライン関数内で定義された静的変数はどうですか?たとえば、「class A {void foo(){static int st_var = 0;}}」をヘッダーファイルに定義し、それをモジュールAとモジュールBに含めます。A/ Bが同じst_varを共有するか、それぞれに独自のコピーがありますか?
カミノ

2
@caminoクラスがエクスポートされる(つまり、で定義される__attribute__((visibility("default"))))場合、A / Bは同じst_varを共有します。ただし、クラスがで定義されている__attribute__((visibility("hidden")))場合、モジュールAとモジュールBは共有されず、独自のコピーを持ちます。
Wei Guo

1
@camino __declspec(dllexport)
ruipacheco
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.