web-dev-qa-db-ja.com

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

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

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

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

  • また、アプリケーションがランタイムダイナミックリンクでモジュールBを使用するとどうなりますか?

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

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

(理由も述べてください)

MSDN からの引用:

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

および here から:

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

ありがとう。

109
Raja

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

何があっても:

  • processには独自のアドレス空間があります。つまり、プロセス間で共有されるメモリはありません(プロセス間通信ライブラリまたは拡張機能を使用しない限り) 。
  • One Definition Rule(ODR)は引き続き適用されます。つまり、リンク時に表示されるグローバル変数の定義は1つしか持てません(静的または動的リンク)。

したがって、ここでの重要な問題は、実際にはvisibilityです。

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

複雑になるのは、externグローバル変数がある場合です。ここで、WindowsとUnixライクなシステムはまったく異なります。

Windows(.exeおよび.dll)の場合、externグローバル変数はエクスポートされたシンボルの一部ではありません。つまり、異なるモジュールは、他のモジュールで定義されたグローバル変数をまったく認識しません。これは、たとえば、DLLで定義されているextern変数を使用することになっている実行可能ファイルを作成しようとすると、リンカーエラーが発生することを意味します。オブジェクトファイル(または静的ライブラリ)にそのextern変数の定義を提供し、both実行可能ファイルとDLLと静的にリンクする必要があります。その結果、2つの異なるグローバル変数(1つは実行可能ファイルに属し、もう1つはDLLに属します)になります。

Windowsでグローバル変数を実際にエクスポートするには、関数のエクスポート/インポート構文に類似した構文を使用する必要があります。

#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グローバル変数(または関数)をエクスポートします。この場合、どこからでも共有オブジェクトファイルにload-timeリンクすると、グローバル変数は共有されます。 。基本的に、Unixライクシステムは、静的ライブラリと動的ライブラリのリンクに実質的に違いがないように設計されています。繰り返しになりますが、ODRはボード全体に適用されます。externグローバル変数はモジュール間で共有されます。つまり、ロードされたすべてのモジュールに1つの定義のみが必要です。

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

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

153
Mikael Persson