コンピューター:C言語講座:共有ライブラリについて
概要
今回はC言語講座の読者の方からの提案で、共有ライブラリを取り上げます。
共有ライブラリは、静的なライブラリが実行形式に合体されるかたちでリンクされるのと違い、実行時に動的にリンクされるものという定義で、しかも他のプロセスと共有するかたちで実行時にリンクされて使われるものです。UNIXでは、プロセス間でファイルを共有するというと、mmap()を思い浮かべますが、まさにこれを使用し、共有ライブラリをマップして動作するものです。
一般のライブラリは、arコマンドによりアーカイブされて、要するにオブジェクトファイルを固めてつくるようなもので、普通は拡張子として「.a」を使用します。共有ライブラリはarコマンドは使用せず、ccコマンドから実行されるldコマンドが作成します。一般的に拡張子として「.so」を使用します。
ccあるいはldコマンドのオプションとしては、Solarisでは、「-G」で、LINUXなどで使用されているgccでは「-G」も互換性のため使えるようですが、「-shared」オプションがそれにあたります。
Windowsでは共有ライブラリは「DLL」としてその拡張子自体の名前でも有名ですが、ここではUNIXでの共有ライブラリの実験を行ってみましょう。
サンプル
まず、共有ライブラリを作成してみます。サンプルは実にシンプルに、printf()で"Shared
Library"と表示するだけのものにしてみました。
---- my_func.c ----
#include <stdio.h>int my_func() { printf("Shared Library\n");return(0); }
では、このソースを共有ライブラリ化してみましょう。
cc -G my_func.c -o libmylib.so
これでlibmylib.soというファイルができます。
つづいて、テスト用にこの関数を呼び出すプログラムをつくります。
---- main.c ----
#include <stdio.h>void main() { my_func(); }
共有ライブラリをリンクして、実行ファイルを作成します。
cc main.c -o main ./libmylib.so
これでmainという実行ファイルが作成され、実行すると、
Shared Library
と表示されるはずです。これだけですと、共有ライブラリとの違いが分かりませんので、my_func.cをちょっと変更してみましょう。
---- my_func.c ----
#include <stdio.h>int my_func() { printf("Shared Library Test\n");return(0); }
表示する文字列をちょっと長くしてみました。ここで、
cc -G my_func.c -o libmylib.so
と、共有ライブラリだけ再構築させ、mainという実行ファイルを実行すると、
Shared Library Test
と、表示内容が変化します。静的なライブラリであればmainを再リンクしないと変化しないものです。
また、libmylib.soを消したり、場所を変えたりして、mainを実行すると、
ld.so.1: main: 重大なエラー: ./libmylib.so: open に失敗しました: ファイルもディレクトリもありません。
という感じのエラーが表示され、実行時に共有ライブラリファイルをオープンしようとしているのが分かります。
lddというコマンドを使用すると、実行ファイルがどの共有ライブラリを使用するかが分かります。
ldd main
と実行すると、
./libmylib.so => ./libmylib.so libc.so.1 => /usr/lib/libc.so.1 libdl.so.1 => /usr/lib/libdl.so.1
と表示されます。このほかにnmコマンドや、trussコマンドでも共有ライブラリの情報が得られますので、Solarisのユーザの方は試してみてください。
なお、今回は./libmylib.soとパスを指定してリンクしましたが、-lmylibと言う感じに指定した場合は環境変数LD_LIBRARY_PATHにしたがってライブラリを探します。したがって、「cc main.c -o main -L./ -lmylib」と指定して作成した場合はLD_LIBRARY_PATHに「./」が無いと見つからないというエラーが出ますので注意してください。
まとめ
実行時に動的にリンクされるというのは実際に実行してみると楽しいものですが、どういう時に使うと良いのか、と言われるとちょっと困る感じです。標準ライブラリ程度に良く使われる関数群はなかなか自分で作っている限りは無いかも知れません。ある程度のプロジェクト規模で、共通に使用する部分で使うと、その部分に変更があった際に全プロセスを再リンクしなくて済むので便利そうです。また、Solarisのデバッグツールでメモリのデバッグを行う際に、プロセスサイズが大きすぎるとできないのですが、この場合も共有ライブラリに分割するとチェック可能になります。いずれにしてもUNIXではWindowsのInstallShieldの用に共有ライブラリまで含めて簡単にエンドユーザがインストールできるようなツールはあまりないと思いますので、エンドユーザに配る際には共有ライブラリのバージョン管理など注意が必要でしょう。