ライブラリ
ライブラリ(Library)は、汎用性の高い複数のプログラムを、再利用可能な形でひとまとまりにしたものである。一般にライブラリは、それ単体ではプログラムとして動作させることはできないので実行ファイルではない。ライブラリは他のプログラムに何らかの機能を提供するコードの集まりと言うことができる。ソースコードの場合と、オブジェクトコード、あるいは専用の形式を用いる場合とがある。たとえば、UNIXのライブラリはオブジェクトコードをarと呼ばれるアーカイバでひとまとめにして利用する。
また、ソフトウェア以外の再利用可能なものの集合について使われることもある。
動的リンク
動的リンク (dynamic linking)は、あるライブラリ内のデータ(コードを含む)を新たな実行ファイルのコンパイル時にコピーすることはなく、ディスク上に別のファイルとして存在している。コンパイル時にリンカが行うのは、その実行ファイルが必要とするのがどのライブラリのどの部分であるか(関数名やインデックス)を記録するだけである。リンク作業の大部分はそのアプリケーションがメモリ上にロードされたときか、実行時である。リンクを行うコードはローダ(loader)と呼ばれ、実際にはオペレーティングシステム (OS)の一部と見なされる。適当な時点でローダは必要なライブラリをディスク上で見つけてプロセスのメモリ空間に(追加のデータ空間と共に)マッピングする。OSによってはプロセスが実行開始する前でないとライブラリをリンクできないものもあるが、多くのOSではプロセス実行時に実際にライブラリを参照したときにリンクすることができる。後者は「遅延読み込み」などと呼ばれる。どちらの場合もライブラリはダイナミックリンクライブラリ、共有ライブラリ、シェアードライブラリなどと呼ばれる。DLL という呼び方は Microsoft Windows 環境で一般的であり、動的ライブラリのファイル拡張子は .dll である。
ローダの処理は、メモリ上の各ライブラリの位置が実際にロードされるまで確定しないため、ちょっとしたトリックを必要とする。ディスク上のファイル内に絶対アドレスを書きこんでおくことはDLL内であっても不可能である。理論的にはメモリにロードされたときにライブラリを参照している部分を全て書き換えて正しいメモリ上の位置を参照するようにすることはできるが、それによって消費される時間とメモリは無視できない。その代わりに多くの動的リンクシステムではアドレス欄が空欄となったシンボルテーブルをコンパイル時に用意する。ライブラリへの参照は全てこのシンボルテーブルを経由して行われる(コンパイラはシンボルテーブルからアドレスを取り出して使うコードを生成する)。メモリにロードされたとき、ローダがこのテーブルを書き換える。
ライブラリも全メソッド(関数、サブルーチン)のテーブルを持っている。ライブラリに入ってくるときは、このテーブルを経由して各ルーチンにジャンプする。これによってライブラリのルーチンコールにオーバヘッドが発生するが、それは無視できるほど小さい。
動的リンカ・ローダは機能面で様々なものがある。いくつかの場合、実行ファイルに格納された明示的なライブラリパスに依存し、ライブラリ名やディスク上の配置を変更するとシステムが動作できなくなる。より一般的な手法としてはライブラリ名だけを実行ファイルに格納し、オペレーティングシステムが何らかのアルゴリズムでディスク上のライブラリを検索する。UNIX系システムでは、ライブラリを探す場所(ディレクトリ)を構成ファイルにリストアップしておく。ライブラリ開発者はそこに書かれたディレクトリにライブラリを配置することを推奨される。しかし、この方法では新しいライブラリをインストールする際に問題が発生しやすく、共通のディレクトリにあまりにも多くのライブラリが置かれることとなって管理を難しくする。Microsoft Windows ではレジストリを使ってActiveX DLL の場所を決めているが、標準DLLでは、
- アプリケーションの実行ファイルの存在するディレクトリ
- SetDllDirectory()で示されるディレクトリ
- システムディレクトリ (NT系ではSystem32)
- 16ビットシステムディレクトリ (System)
- Windowsディレクトリ
- カレントディレクトリ
- PATH環境変数
で示されるディレクトリを探す(古いバージョンではカレントディレクトリが2番目だった)[1]。OPENSTEPはもっと柔軟なシステムを使用していて、ライブラリの探索リストを保持している。しかし不正なDLLが探索の上位に置かれていると実行ファイルは不正動作する可能性がある。Windowsでは、これが「DLL地獄」(DLL hell)と呼ばれ、よく知られている問題である。
Windows XPからは、Side-by-Sideアセンブリ (DLL署名, WinSxS)というメカニズムが追加された。これは動的リンク時にライブラリのファイル名ではなく、ライブラリにつけられた署名によってリンクすべきライブラリを決定するものである。これにより、同じファイル名を持つが異なる実装を持つライブラリを同時に使い分ける事ができる。よくあるパターンとして、ソースコードから改変・ビルドされたランタイムライブラリをシステムにインストールする場合にこのメカニズムが有効に働く。システムにインストールされたライブラリはライブラリ探索リスト上比較的上位に存在するが、署名が一致するプログラムにのみロードされるのでDLL地獄は今後解消されるであろうと考えられる。しかし、この機構には一つの弱点がある。それはシステムライブラリをオーバーライドして独自機能を実装する時、この機構は役に立たない方向へ働く。その様な実装をする時には、故意にマニフェスト機能を無効にしてライブラリを作らなくてはならない。もっとも、そのようなアプローチは、システムファイル保護機能が搭載されたWindows 2000のリリース時点で時代遅れであり、Windows Vistaに至っては管理者と言えどもシステムライブラリを書き換える事は出来なくなっている。
動的ライブラリの起源は定かではないが、少なくとも1960年代後半のMTS (Michigan Terminal System)まで遡ることができる("A History of MTS", Information Technology Digest, Vol. 5, No. 5)。
動的読み込み
これは動的リンクの下位カテゴリであり、コンパイル時にリンクされた以外のダイナミックリンクライブラリを実行中のプログラムが明示的にロードすることである。この場合、ライブラリはプラグインモジュールとして使われるのが一般的で、表計算プログラムのadd-inや特定機能を実現するインタプリタなどが典型的である。
動的ライブラリをサポートしているシステムは動的読み込みAPIもサポートしているのが一般的である。例えばWindowsは LoadLibrary() と GetProcAddress() を用意していて、UNIX系システムでは dlopen() と dlsym() を用意している。いくつかの開発システムではこの処理を自動化している。
リモート・ライブラリ
もうひとつのライブラリの形態として完全に分離された実行ファイルをRPCと呼ばれる方法で接続するものがある。このアプローチではオペレーティングシステムの再利用が最大に生かされる。つまり、ライブラリサポートのためのコードはアプリケーションサポートのコードやセキュリティサポートのコードと共通化できる。さらに、このライブラリはネットワークを経由した別のマシン上に存在しても構わない。
欠点はライブラリコールの度に無視できないオーバヘッドが発生することである。RPCは非常にコストがかかり、可能な限り排除されてきた。しかし、このアプローチは特定分野で一般化しつつある。特にクライアントサーバシステムやEnterprise JavaBeansのようなアプリケーションサーバで一般的である。
共有ライブラリ
動的か静的かとは別に、ライブラリはプログラム間で共有される方式でも分類される。動的ライブラリは何らかの共有をサポートしており、複数のプログラムが同時に同じライブラリを使用することができる。静的ライブラリは各プログラムにリンクされるため、共有することはできない。
共有ライブラリ (shared library)は、やや曖昧な用語であり、ふたつの概念を含む。第一はディスク上のコードを複数の無関係なプログラムが共有することを意味する。第二の概念はメモリ上のコードの共有であり、ライブラリのロードされた物理メモリページが複数のプロセスのアドレス空間にマップされ、同時にアクセスされることを意味する。一般に後者を共有ライブラリと称するのが推奨され、この方式には様々な利点がある。例えばOPENSTEPでは、アプリケーションの多くは数百Kバイトで即座にロード可能であり、その機能の大部分はライブラリ上に実装されていて、共有可能であるためにOSが別のプログラム用にメモリにロードしたコードイメージがそのまま使用できる。しかし、マルチタスク環境で共有されるコードは特別な配慮が必要であり、そのために性能が若干低下する。
メモリ上の共有ライブラリはUNIXでは位置独立コード (PIC) を使って実現される。これは柔軟なアーキテクチャだが複雑であり、Windowsなどでは使われていない。Windowsなどは、DLL毎にマップすべきアドレスを事前に決めておくなどしてメモリ上で共有可能にしている。WindowsのDLLはUNIXから見れば共有ライブラリではない。(訳注:UNIXでもライブラリのマップすべきアドレスを決めている場合がある。ただしそれは性能向上目的であり、基本的にはPIC化されている。)
最近のOSでは共有ライブラリは通常の実行ファイルと同じ形式になっている。これにはふたつの利点がある。第一はひとつのローダで両方をロードできる。それによってローダが若干複雑化するが、十分コストに見合う程度である。第二はシンボルテーブルさえあれば実行ファイルを共有ライブラリとして使うことができる点である。このようなファイル形式として、ELF (UNIX)と PE (Windows)がある。また、Windowsではフォントなどのリソースも同じファイル形式になっている。OPENSTEPでもほとんど全てのシステムリソースが同じファイル形式になっている。
DLLという用語はWindowsやOS/2で主に使われる。UNIXでは「共有ライブラリ」が一般的である。
マルチスレッド環境下でライブラリを使用するにあたっては、別の共有問題が発生する。ライブラリルーチンがデータ領域としてスタックのみを使う場合は問題ないが、ライブラリ内のデータ領域を使う場合、そのデータ領域がスレッド毎に用意されていないことが多い。したがって、そのようなライブラリルーチンを使う場合、実行ファイル側で同時に複数のスレッドが同じライブラリルーチンを使わないように注意しなければならないことがある。
オブジェクト・ライブラリ
1980年代終盤に開発された動的リンクは1990年代初期にはほとんどのオペレーティングシステムで使用可能となった。ほぼ同時期にオブジェクト指向プログラミング (OOP)が市場に出回り始めた。OOP は従来のライブラリが提供していなかった情報を必要とした。それは、あるオブジェクトが依存しているオブジェクトのリストである。これはOOPの継承という機能の副作用であり、あるメソッドの完全な定義は複数の場所に分散して配置される可能性がある。これは単純化すればライブラリ間の依存関係ということになるが、真のOOPシステムではコンパイル時には依存関係が明らかでなく、そのために様々な解決方法が登場した。
同じころ、多層構造のシステムの考え方も出てきた。デスクトップコンピュータ上の表示プログラムが汎用コンピュータやミニコンピュータの記憶装置や演算機能を利用するものである。例えばGUIベースのコンピュータがミニコンピュータにメッセージを送り、表示すべき膨大なデータの一部を得るというものが考えられた。RPCは既に使われていたが、それは標準化されていなかった。
主要なミニコンおよび汎用機メーカーがこれら二つの問題に関してプロジェクトを結成し、どこでも使えるOOPライブラリ形式を開発した。このようなシステムをオブジェクト・ライブラリ (object library)と呼んだり、リモートアクセスが可能ならば分散オブジェクト (distributed objects)と呼ぶ。マイクロソフト社のCOMは分散機能のないオブジェクトライブラリであり、DCOMはリモートアクセスを可能としたバージョンである。
一時期、オブジェクトライブラリはプログラミングの世界の「次の大きな出来事」とされた。様々なシステムが開発され、競争も激化した。例をあげると、IBMのSOM/DSOM、サン・マイクロシステムズのDOE、NeXTのPDO、DECの ObjectBroker、マイクロソフトのComponent Object Model (COM/DCOM)、そして様々なCORBAベースのシステムがある。
結局、OOPライブラリは次の大きな出来事ではなかった。マイクロソフトのCOMとNeXT(現在はアップル)のPDO以外は、ほとんど使われることも無くなったのである。
Javaではオブジェクトライブラリとして主にjarが使われている。その中には(圧縮された)クラスのバイトコード形式が格納されていて、Java-VMや特殊なクラスローダがそれをロードする。
ネーミング
- GNU/Linux、Solaris、BSDの派生OS:
/lib
、/usr/lib
、/usr/local/lib
などのフォルダに置かれるlibfoo.a
やlibfoo.so
といったファイルはダイナミックリンクライブラリである。ファイル名は常にlib
で始まり、.a
(静的ライブラリ)か.so
(ダイナミックリンクライブラリ)で終わる。オプションとしてインターフェイス番号が付与される場合がある。例えば、libfoo.so.2
はlibfoo
ライブラリの二番目のメジャーなインターフェイス番号の付いたダイナミックリンクライブラリである。古いUNIXではマイナー番号も使っている場合がある (libfoo.so.1.2
)が、最近のUNIXではメジャー番号しか使っていない。動的にロードされたライブラリは/usr/libexec
などのディレクトリに置かれる。ライブラリのディレクトリにある.la
ファイルは libtool アーカイブである。 - Mac OS X:静的ライブラリの命名法はBSDを踏襲していて、
.dylib
の代わりに.so
を使うことも出来る。しかし、多くの動的ライブラリは「バンドル」 (bundles)と呼ばれる特別な場所に置かれ、ライブラリに関連するファイルやメタデータもそこに置かれる。例えば、"My Neat Library" というライブラリは "My Neat Library.framework" というバンドルに実装される。 - Microsoft Windows:
*.LIB
というファイルは静的ライブラリで、*.DLL
というファイルはダイナミックリンクライブラリである。インターフェイスのリビジョンはファイル内に書きこまれるか、COMインターフェイスを使って抽象化される。また、.NET アセンブリについては、内部のマニフェストに記述される。
脚注
関連項目
- ソフトウェア工学
- 動的リンク
- 静的リンク
- アプリケーションフレームワーク
- Application Programming Interface
- ダイナミックリンクライブラリ (DLL) - Microsoft Windowsの動的ライブラリ
- アセンブリ
- グローバル アセンブリ キャッシュ
- GNU Libtool
- 標準Cライブラリ