共有オブジェクト(.so)、静的ライブラリ(.a)、DLL(.so)の違いは?


273

Linuxのライブラリに関していくつかの議論に参加しており、いくつかのことを確認したいと思います。

アプリケーションを構築するときにライブラリを使用する方法は2つあるというのは私の理解です(間違っている場合は修正してください。後で投稿を編集します)。

  1. 静的ライブラリー(.aファイル):リンク時に、ライブラリー全体のコピーが最終アプリケーションに入れられるため、ライブラリー内の関数は常に呼び出し側アプリケーションで使用できます。
  2. 共有オブジェクト(.soファイル):リンク時に、オブジェクトは対応するヘッダー(.h)ファイルを介してAPIに対して検証されるだけです。ライブラリは、実行時まで実際には使用されません。

静的ライブラリの明らかな利点は、アプリケーション全体を自己完結できることです。動的ライブラリの利点は、「。so」ファイルを置き換えることができることです(つまり、セキュリティのために更新する必要がある場合)バグ)ベースアプリケーションを再コンパイルする必要はありません。

どちらも「.so」ファイルですが、共有オブジェクトとダイナミックリンクライブラリ(DLL)を区別する人がいると聞いています。Linuxまたは他のPOSIX準拠OS(つまり、MINIX、UNIX、QNXなど)でのC / C ++開発に関して、共有オブジェクトとDLLの間に違いはありますか?(これまでのところ)1つの重要な違いは、共有オブジェクトは実行時に使用されるだけであり、アプリケーション内でdlopen()呼び出しを使用してDLLを最初に開く必要があるということです。

最後に、一部の開発者が「共有アーカイブ」について言及しているのを聞いたことがあります。これは、私の理解では静的ライブラリでもありますが、アプリケーションで直接使用されることはありません。代わりに、他の静的ライブラリが「共有アーカイブ」にリンクして、共有アーカイブから構築中の静的ライブラリに一部(すべてではない)の関数/リソースをプルします。

よろしくお願いします。

更新


これらの用語が私に提供された状況では、Linuxを学習する必要があるWindows開発者のチームが使用した事実上誤った用語でした。私はそれらを修正しようとしましたが、(正しくない)言語の規範が行き詰まりました。

  1. 共有オブジェクト:プログラムの起動時にプログラムに自動的にリンクされ、スタンドアロンファイルとして存在するライブラリ。ライブラリはコンパイル時にリンクリストに含まれます(つまりLDOPTS+=-lmylib、という名前のライブラリファイルの場合mylib.so)。ライブラリは、コンパイル時とアプリケーションの起動時に存在している必要があります。
  2. 静的ライブラリ:アプリケーションコードと、プログラムのビルド時にプログラムに自動的にリンクされるライブラリコード、および両方を含む最終バイナリを含む単一(大きい)アプリケーションのビルド時に実際のプログラム自体にマージされるライブラリメインプログラムとライブラリ自体は、単一のスタンドアロンバイナリファイルとして存在します。ライブラリは、コンパイル時にリンクリストに含まれます(つまりLDOPTS+=-lmylib、mylib.aという名前のライブラリファイルの場合)。ライブラリはコンパイル時に存在している必要があります。
  3. DLL:基本的に共有オブジェクトと同じですが、ライブラリはコンパイル時にリンクリストに含まれるのではなく、dlopen()/ dlsym()コマンドを介してロードされるため、プログラムをコンパイルするためにライブラリがビルド時に存在する必要はありません。また、ライブラリはdlopen/ dlsym呼び出しが行われたときにのみ必要になるため、アプリケーションの起動時またはコンパイル時(必ずしも)存在する必要はありません
  4. 共有アーカイブ:基本的に静的ライブラリと同じですが、「export-shared」フラグと「-fPIC」フラグを使用してコンパイルされます。ライブラリはコンパイル時にリンクリストに含まれます(つまりLDOPTS+=-lmylibS、という名前のライブラリファイルの場合mylibS.a)。2つの違いは、共有オブジェクトまたはDLLが共有アーカイブを独自のコードに静的にリンクし、共有オブジェクトの関数を他のプログラムで使用するだけでなく、他のプログラムで使用できるようにする場合に、この追加のフラグが必要になることです。 DLLの内部。これは、誰かが静的ライブラリを提供し、それをSOとして再パッケージ化したい場合に役立ちます。ライブラリはコンパイル時に存在している必要があります。

追加アップデート

DLL」と「shared library」の違いは、私が当時働いていた会社での(怠惰で不正確な)口語表現(Windows開発者はLinux開発に移行せざるを得ず、用語は行き詰まっている)であり、上記の説明に忠実でした。

さらに、S「共有アーカイブ」の場合、ライブラリ名の後に続く「」リテラルは、その会社で使用されている規則であり、業界では一般的ではありませんでした。


14
.aファイル、「」実際に「archove」の略で、それは単にオブジェクトファイルのアーカイブです。最新のリンカは、whileライブラリを含める必要がなく、必要なアーカイブ内のオブジェクトファイルだけでなく、参照されるオブジェクトファイル内のコード/データのセクションを使用するだけで十分です。
一部のプログラマー、

4
DLLは単なるWindowsの用語です。uniceでは使用されません。
R .. GitHub STOP HELPING ICE



2
もちろん@DevNull "arch i ve"。:)
一部のプログラマー、

回答:


94

DLLと共有オブジェクトは同じものを単に異なる用語であると常に思っていました-WindowsはDLLをDLLと呼びますが、UNIXシステムでは共有オブジェクトであり、一般的な用語-動的リンクライブラリ-両方をカバーします( UNIXで.soを開くとdlopen()、「ダイナミックライブラリ」の後に呼び出されます)。

実際には、アプリケーションの起動時にのみリンクされますが、ヘッダーファイルに対する検証の概念は正しくありません。ヘッダーファイルは、ライブラリを使用するコードをコンパイルするために必要なプロトタイプを定義しますが、リンク時に、リンカーはライブラリ自体を調べて、必要な関数が実際にそこにあることを確認します。リンカは、リンク時に関数本体をどこかで見つける必要があります。そうしないと、エラーが発生します。正しく指摘すると、プログラムのコンパイル後にライブラリ自体が変更された可能性があるためです。これは、ABIの変更が古いバージョンに対してコンパイルされた既存のプログラムを破壊するものであるため、ABIの安定性がプラットフォームライブラリで非常に重要である理由です。

スタティックライブラリは、プロジェクトのコンパイルの一部として自分で構築しているものと同じように、コンパイラから直接オブジェクトファイルをバンドルしたものであり、まったく同じ方法でリンカに取り込まれ、供給されます。未使用のビットはまったく同じように落ちました。


1
Linuxで私が見るいくつかのプロジェクトが ".so"ファイル内の関数にアクセスするためにdlopen()呼び出しを使用しなければならないのに、なぜそうする必要がないプロジェクトがあるのですか?ちなみにありがとうございます!
Cloud

9
そうしないものは、プロセスローダー、つまりLinuxのエルフローダーによって関数が渡されます。dlopenは、アプリケーションが、コンパイル時にそこになかった.soまたは.dllを開いて使用したり、プラグインなどの追加機能を追加したりする場合に存在します。
rapadura

しかし、ビルド時に.soが存在しない場合、アプリケーションはまったくコンパイルされませんか?.soがまったく存在しない状態で最終プログラムをビルドするようにリンカーに強制することは可能ですか?ありがとうございました。
Cloud

1
私はそれが.soからの関数をどのように使用するかに依存すると思いますが、ここでこれに関する私の知識が停止します:/良い質問。
rapadura

1
dlopen()とその関数ファミリーに関しては、これはプログラムでdllを開いたり閉じたりするために使用されるため、アプリケーションの実行全体を通してメモリにロードする必要がないことを理解しています。それ以外の場合は、ライブラリをロードすることを、コマンドライン引数(別名makefile)でリンカーに指示する必要があります。実行時にロードされ、アプリケーションが終了するまでメモリにロードされたままになります。OSレベルで発生する可能性のあるものは他にもありますが、アプリケーションに関する限り、これはおおまかに起こります。
テイラー価格

198

スタティックライブラリ(.A)は、リンカーによって生成される最終的な実行可能ファイルに直接リンクすることができ、それはその中に含まれているライブラリであり、実行可能ファイルが展開されるシステムにライブラリを有する必要はありません。

共有ライブラリ(.soの)実行可能ファイルを起動し、必要に実行ファイルが展開されているシステムに存在するときにロードされ、リンクされたが、最終的な実行可能ファイルに埋め込まれていないライブラリです。

Windows(.dll)ダイナミックリンクライブラリはLinuxの共有ライブラリ(.so)に似ていますが、OS(WindowsとLinux)に関連する2つの実装にはいくつかの違いがあります。

A DLLは、エクスポートと内部:関数の二種類を定義することができます。エクスポートされた関数は、それらが定義されているDLL内からだけでなく、他のモジュールからも呼び出されることを意図しています。内部関数は通常、それらが定義されているDLL内からのみ呼び出されることを目的としています。

Linux のSOライブラリーは、すべてのシンボルが問い合わせプロセスで使用できるため、エクスポート可能なシンボルを示すための特別なexportステートメントを必要としません。


1
+1簡単な説明。DLLで関数が「内部」として宣言されている場合、それはライブラリの外部から呼び出すことができないことを意味しますか?
マイク

23
すべてのシンボルがSOライブラリーで使用できるとは限りません。ライブラリユーザーがすべてのシンボルを表示する正当な理由がないため、非表示のシンボルは可能であり、推奨されます。
Zan Lynx 2013年

3
FYI:G ++が持っている__attribute__選択的に「輸出」のシンボルの構文を:#define DLLEXPORT __attribute__ ((visibility("default"))) #define DLLLOCAL __attribute__ ((visibility("hidden")))
ブライアン・ハーク

33

WindowsのDLLの詳細について詳しく説明して、これらの謎を* NIXランドにいる私の友達に明らかにするのに役立てることができます...

DLLは共有オブジェクトファイルのようなものです。どちらもイメージであり、それぞれのOSのプログラムローダーによってメモリにロードする準備ができています。画像には、リンカーとローダーが必要な関連付けを行い、コードのライブラリを使用するのに役立つメタデータのさまざまなビットが付随しています。

Windows DLLにはエクスポートテーブルがあります。エクスポートは、名前またはテーブルの位置(数値)で行うことができます。後者の方法は「古い」と考えられており、はるかに壊れやすいです。DLLを再構築してテーブル内の関数の位置を変更すると、障害が発生しますが、エントリポイントのリンクが名前である場合、実際の問題はありません。したがって、それを問題として忘れてください。ただし、サードパーティベンダーのライブラリなどの「恐竜」コードを使用している場合は問題があることに注意してください。

Windows DLLは、EXE(実行可能アプリケーション)の場合と同じように、コンパイルとリンクによって構築されますが、DLLはスタンドアロンではなく、SOがアプリケーションによって動的ロードまたはリンク時バインディングによって(SOへの参照はアプリケーションバイナリのメタデータに埋め込まれ、OSプログラムローダーは参照されたSOを自動ロードします)。DLLは、SOが他のSOを参照できるのと同じように、他のDLLを参照できます。

Windowsでは、DLLは特定のエントリポイントのみを使用可能にします。これらは「エクスポート」と呼ばれます。開発者は、特別なコンパイラキーワードを使用して、シンボルを外部から(他のリンカーおよびダイナミックローダーに対して)可視にするか、またはDLL自体がリンクされているときにリンク時に使用されるモジュール定義ファイルにエクスポートをリストできます。作成されています。現代の習慣は、シンボル名をエクスポートするためにキーワードで関数定義を装飾することです。現在のコンパイルユニット外のDLLからインポートされるものとしてそのシンボルを宣言するキーワードでヘッダーファイルを作成することも可能です。詳細については、キーワード__declspec(dllexport)および__declspec(dllimport)を参照してください。

DLLの興味深い機能の1つは、DLLが標準の「ロード/アンロード時」ハンドラー関数を宣言できることです。DLLがロードまたはアンロードされると、DLLは初期化またはクリーンアップを実行できます。これは、デバイスドライバーや共有オブジェクトインターフェイスなどのDLLをオブジェクト指向のリソースマネージャーとして適切にマップします。

開発者が既にビルドされたDLLを使用する場合、DLLの作成時にDLL開発者が作成した「エクスポートライブラリ」(* .LIB)を参照するか、実行時にDLLを明示的にロードしてリクエストする必要があります。 LoadLibrary()およびGetProcAddress()メカニズムを介した名前によるエントリポイントアドレス。ほとんどの場合、LIBファイル(DLLのエクスポートされたエントリポイントのリンカーメタデータを含む)に対してリンクすることで、DLLが使用されます。動的ローディングは、通常、プログラムの動作(アドオンまたは後で定義される機能(別名「プラグイン」)にアクセスする)に「ポリモーフィズム」または「実行時設定可能性」を実装するために予約されています。

Windowsでの方法では、時々混乱が生じることがあります。システムは.LIB拡張子を使用して、通常の静的ライブラリ(POSIX * .aファイルなどのアーカイブ)と、リンク時にアプリケーションをDLLにバインドするために必要な「エクスポートスタブ」ライブラリの両方を参照します。したがって、*。LIBファイルに同じ名前の* .DLLファイルがあるかどうかを常に確認する必要があります。そうでない場合、*。LIBファイルが静的ライブラリアーカイブであり、DLLのバインディングメタデータをエクスポートしない可能性があります。


4

静的ファイルはリンク時にアプリケーションにコピーされ、共有ファイルはリンク時に検証され、実行時にロードされるという点で正しいです。

dlopen呼び出しは、アプリケーションが実行時に代わって実行する場合、共有オブジェクトだけでなく、それ以外の場合、アプリケーションの起動時に共有オブジェクトが自動的にロードされます。DLLと.soは同じものです。dlopenは、プロセスにさらにきめの細かい動的ロード機能を追加するために存在します。DLLを開いたり使用したりするために、dlopenを自分で使用する必要はありません。これは、アプリケーションの起動時にも行われます。


より多くのロード制御のためにdlopen()を使用する1つの例は何でしょうか?SO / DLLが起動時に自動ロードされる場合、たとえば、dlopen()はそれを閉じて、別のアクセス許可または制限付きで再度開きますか?ありがとうございました。
クラウド

1
dlopenはプラグインまたは同様の機能用だと思います。許可/制限は自動ロードの場合と同じである必要があります。いずれにしても、dlopenは依存ライブラリを再帰的にロードします。
rapadura

DLLと.soまったく同じではありませんこの回答を
Basile Starynkevitch
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.