1. ホーム
  2. c++

[解決済み] 共有ライブラリを動的リンクした場合、共有ライブラリ内のグローバル変数とスタティック変数はどうなりますか?

2022-04-26 06:46:31

質問

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

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

  • このアプリケーションで、ロードタイム・ダイナミックリンクを行うモジュールAを使用する場合はどうなりますか?DLLにはグローバルとスタティックのためのセクションがあると思います。オペレーティングシステムはそれらをロードしますか?もしそうなら、それらはどこにロードされるのでしょうか?

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

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

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

(理由も併せて記載してください)

引用元 MSDN :

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

とから これ :

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

ありがとうございます。

どのように解決するのですか?

これはWindowsとUnix系システムの違いとして、かなり有名な話です。

何があっても

  • それぞれの プロセス は独自のアドレス空間を持ち、プロセス間でメモリが共有されることはありません(プロセス間通信ライブラリや拡張機能を使用した場合を除く)。
  • 1つの定義ルール (ODR) はまだ適用されます。つまり、リンク時に見えるグローバル変数の定義は 1 つだけです (静的リンクまたは動的リンク)。

つまり、ここでの重要な問題は、本当に 視認性 .

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

複雑になるのは extern グローバル変数です。ここで、WindowsとUnix系は全く別物です。

Windows(.exeと.dll)の場合は extern グローバル変数はエクスポートされたシンボルの一部ではありません。つまり、異なるモジュールは、他のモジュールで定義されたグローバル変数を一切意識することがないのです。つまり、例えば、あるモジュールで定義されたグローバル変数を使うはずの実行ファイルを作ろうとすると、リンカエラーが発生します。 extern というのも、DLLで定義された変数を使用することはできないからです。オブジェクトファイル(またはスタティックライブラリ)にextern変数を定義して、それを静的にリンクする必要があります。 ともに その結果、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など)の場合、ダイナミックライブラリは、拡張子が"shared objects"と呼ばれます。 .so エクスポートオール extern グローバル変数(または関数)を使用します。この場合、もしあなたが ロードタイム は、どこからでも共有オブジェクトファイルにリンクされるため、グローバル変数は共有される、つまり、1つのものとしてリンクされることになります。基本的にUnix系のシステムでは、スタティックライブラリでリンクしてもダイナミックライブラリでリンクしても、ほとんど差がないように設計されています。ここでも、ODRは全面的に適用されます。 extern グローバル変数はモジュール間で共有されます。つまり、ロードされるすべてのモジュールで1つの定義しか持たないはずです。

最後に、WindowsまたはUnixライクなシステムの場合、どちらのケースでも 実行時 ダイナミック・ライブラリのリンク、すなわち LoadLibrary() / GetProcAddress() / FreeLibrary() または dlopen() / dlsym() / dlclose() . その場合、使用したいシンボルへのポインタをそれぞれ手動で取得する必要があり、それには使用したいグローバル変数も含まれます。グローバル変数については GetProcAddress() または dlsym() 関数と同じように、グローバル変数がエクスポートされたシンボルリストの一部であれば、(前の段落の規則によって)大丈夫です。

そしてもちろん、必要な最後の注意として。 グローバル変数は避けるべき . そして、あなたが引用した文章(物事が "曖昧であることについて)は、まさに私が先ほど説明したプラットフォーム固有の差異を指していると思います(ダイナミックライブラリは実際には C++ 標準で定義されておらず、これはプラットフォーム固有の領域です、つまり信頼性と移植性がはるかに低いということです)。