HP-UX General
1819796 メンバー
3179 オンライン
109607 解決策
新規ポスト

共有ライブラリのエントリポイントについて

 
nacky
時折のコントリビューター

共有ライブラリのエントリポイントについて

いつも拝見させていただいております。

共有ライブラリを作成しているのですが、

資源の初期化を、ライブラリがロードされた時点で行いたいと考えています。

WindowsのDllMain()のような、

共有ライブラリのエントリポイントを提供する関数はないのでしょうか?

10件の返信10
テレコム担当者
尊敬されているコントリビューター

共有ライブラリのエントリポイントについて

>資源の初期化を、ライブラリがロードされた時点で行いたいと考えています。

DLLエントリーポイントは詳しくは分かりませんが、

Windows OS 特有なのでしょう。

UNIX では共有ライブラリの初期化などの概念はありません。

>WindowsのDllMain()のような、

>共有ライブラリのエントリポイントを提供する関数はないのでしょうか?



ないと思います。

man shl_load を読んでみた方が良いです。

nadachi
レギュラーアドバイザー

共有ライブラリのエントリポイントについて

私も共有ライブラリをロードしたときに自動で呼ばれる関数、てのはHP-UXでは聞いたことがありません。これは共有ライブラリがディスクのファイルから初めてロードされるときに一回だけ実行されるような関数ということでしょうか。

"初めて実行される" をきっかけに出来ないかと、

以下のようなのを試しました。例えば、

---- sub.c

static int flag = 0;

sub()

{

if (flag == 0) {

flag = 1;

printf("initialized\n");

}

printf("sub is called by pid %d\n",

getpid(0) );

}

これから、

$ cc +z -c sub.c

$ ld -b -o sub.sl sub.o

で共有ライブラリを作り、

---- main.c

#include

main()

{

sub();

sleep(10);

}

というmainをコンパイル。

$ cc main.c sub.sl

できたa.outを、2つのwindow で順に走らせる(つまり、一つ前のが終わらないうちにもう一つ並列実行する)

$ ./a.out

initializing

sub is called by pid 27811

$ ./a.out

initializing

sub is called by pid 27812

ということで、2つのプロセスがこのライブラリを共有しているわけですが、sub.sl中の static 変数 "flag" は、プロセスごとに(異なった)実体があることが分かります。(もし本当にこの変数の実体が1つしかないなら、2つめに起動したプロセスでは flag が 1になっており、"initializing"は表示されないはずです。

 このことから、もし、共有ライブラリで、ロードされた時点で自動的に(一回)起動される関数があったとしても、リソースをそのライブラリで宣言した変数で管理する限りにおいて、それが見えるのはロードのきっかけとなったプロセスだけでしょう。

 そうではなく、共有ライブラリがプロセスによって

"ロード"されるごとに、何らかの初期化ルーチンが

走るのを期待したい、ということであれば、ライブラリを共有ライブラリとしてリンクした場合と、スタティックライブラリとしてarchiveリンクした場合と、動作が異なることになります。

 テレコム担当者さんが書いたように、もし、共有ライブラリを shl_load()やdlopen()を使って明示的に読み込むのであれば (通常は dld が自動でやります)

読み込んだ後、例えば、"ライブラリ名_init" という関数名があればそれを一回だけ呼び出す、というロジックを組み込むことは可能でしょう。

ただしこの場合でも、リソースの管理を共有ライブラリのグローバル変数に置くのでは他のプロセスには伝わりません。(共有メモリセグメントを割り付けるところまでやれば、複数プロセスから共有できるリソースにはなりますが、大変そう。)

isao
頻繁なアドバイザー

共有ライブラリのエントリポイントについて

http://docs.hp.com/ja/B2355-90657/ch06s04.html#d0e18054

上記URLに共有ライブラリのイニシャライザーという項目があります。

共有ライブラリをロード・アンロードする際に実行するルーチンを指定できるようです。

例えば、以下の様なります。

mylib.c:

#include

#include

void my_init(shl_t hdl, int op)

{

if (op) {

printf("my library is now loading\n");

} else {

printf("my library is now unloading\n");

}

}

my_func(int i) {

return (i < 0)?i:-i;

}

t.c:

#include

#include

main()

{

int (*func)(int);

shl_t hdl;

hdl = shl_load("mylib.sl", BIND_IMMEDIATE, 0);

shl_findsym(&hdl, "my_func", TYPE_PROCEDURE, (void *)&func));

printf("%d\n", (*func)(-13));

shl_unload(hdl);

}

$ cc -c +z mylib.c

$ ld -b -o mylib.sl +I my_init mylib.o

$ cc t.c

$ a.out

my library is now loading

-13

my library is now unloading

nacky
時折のコントリビューター

共有ライブラリのエントリポイントについて

ご返信ありがとうございます。

ライブラリロード時の初期化はisaoさんの方法で

実現できそうです。

質問に挙げ忘れましたが、

DllMain()は、DLLをロードしているプロセスが

スレッドを作成した場合にも呼び出されるのですが、

UNIXでこれと同等の処理を行うことは

不可能なのでしょうか?

nadachi
レギュラーアドバイザー

共有ライブラリのエントリポイントについて

 なるほど、ldコマンドで共有ライブラリを作るときに +I オプションで固有の初期化関数名を指定するわけですね。

 "isao"さんのサンプルはshl_load()まで使った凝ったものなので、単純にした追試をしました。

mylib.c

#include

#include

static int flag = 1;

void

my_init(shl_t hdl, int op)

{

if (op) {

printf("my library is now loading\n");

++flag;

} else {

printf("my library is now unloading\n");

}

}

my_func(int i)

{

printf("my func : %d, flag : %d\n", i, flag);

}

で、main.cは単に、

#include

main()

{

my_func(10);

sleep(10);

}

にしました。

$ cc +z -c mylib.c

$ ld -b -o mylib.sl +I my_init mylib.o

$ cc main.c mylib.sl

で実行形式ファイルを作り、a.outを2つのwindowで順に実行したところ、

$ ./a.out

my library is now loading

my func : 10, flag : 2

で、もう一つ並列に実行しても、

$ ./a.out

my library is now loading

my func : 10, flag : 2

となる。ですので初期化関数は、共有ライブラリがファイルからメモリにロードされる時点で一回だけ実行される、という意味合いのものではなく、プロセスにアタッチされる時点でそれぞれ一回実行される、

共有ライブラリ中のデータは、プロセスごとに異なった空間におかれる、ということが分かります。

 nackyさんのおっしゃる "DLLをロードしている

プロセスがスレッドを作成するごとに呼び出される。" ということですが、うーん、HP-UXとWindows OSとで、プロセスとスレッドという用語の意味が若干異なるような気が。

 上のサンプルプログラムmain()でpthread_create()でthreadを作っても共有ライブラリの再度のロードや初期化は起こりません。一方、このmain()で fork/exec を使って新しいプロセスを作るとき、新しいプロセスが同じ共有ライブラリとリンクしてあれば、それをアタッチするときに初期化ルーチンは呼ばれるはずです。

 ですので、

"資源の初期化を、ライブラリがロードされた時点で行いたい" ということが、"共有ライブラリが初めてファイルからメモリに読まれた時点で一回だけ実行する関数を持ちたい" ということであれば、"ファイルからメモリへ” のきっかけの部分は ld の +I で実現できますが、”一回だけ" というのが苦しい。外部(共有メモリ? ファイル?) に何らかのフラグを持たないといけないと思います。 (同様に unloadされたとき、最後のunloadで一回だけ呼ぶ、てのも何らかの細工を要するような)

nacky
時折のコントリビューター

共有ライブラリのエントリポイントについて

ご返信ありがとうございます。

どうやら、

資源初期化の契機を提供するI/Fを

ライブラリに持たせるのが1番無難なように思われますので

とりあえず、それを検討したいと思います。

念のため質問のほうは未解決にしておきますので、

情報お持ちの方は、引き続きご提供の程、宜しくお願いいたします。
nadachi
レギュラーアドバイザー

共有ライブラリのエントリポイントについて

だいぶ話題から外れてしまいますが。DllMain()に

関して。M社のサイトからあんまり引用するのはまずいので,下手な訳ですが...

DllMain

DllMain関数は、ダイナミックリンクライブラリ(DLL)中で、オプションのエントリポイントである。もしこの関数が(DLLの定義中で)使われると、processやthreadが初期化、終了したとき,または、LoadLibrary, FreeLibraryの呼び出し時にsystem(OSのことか?)から呼び出される。

...

fdwReason

DLL_PROCESS_ATTACH

DLL_THREAD_ATTACH 現行processが新しい

threadを作っている。systemは現在このprocessに

attachされているすべてのDLLのこのエントリポイントを(fdvReason=DLL_THREAD_ATTACHにして)呼び出す。

DLL_THREAD_DETACH

DLL_PROCESS_DETACH

 ということは、HP-UX では ld の +I entty_point

でDLL_PROCESS_ATTACH/DLL_PROCESS_DETACHに相当することは実現できそうですが、DLL_THREAD_ATTACH/DLL_THREAD_DETACH は、... 相当する機能をつけるとすれば、

-- thread を作るときには pthread_create()の3番目の引数で関数を指定するわけですから、その関数の頭で初期化動作を行えば, DLL_THREAD_ATTACHに相当する動作は出来る。

-- threadが pthread_exit()を呼んで終わるときは、

後始末関数を明示的に呼べますが、非同期cancelなどで終わるのがある場合は、pthread_cleanup_push()関数で、clean up関数を登録しておけば、thread終了時に呼んでくれるので、DLL_THREAD_DETACHに相当する動作も実現できます。

(それでも "...is called by the system when processs and threads are initialized..." が良く分からない。OSが呼ぶのですかねえ。HP-UXでは、プロセスのコンテキストでdynamic loaderが呼んでくれるはず。だから"system wide" のリソース割付は

単に変数に値を入れるだけでは駄目で、そのプロセスにしか有効ではない。複数プロセス間で共通リソースとして持つには、共有メモリか、ファイルか、(またはmemory mapped fileか?) で持つことになります。)

nacky
時折のコントリビューター

共有ライブラリのエントリポイントについて

>-- thread を作るときには

>pthread_create()の3番目の引数で関数を指定するわけですから、

>その関数の頭で初期化動作を行えば,

>DLL_THREAD_ATTACHに相当する動作は出来る。

pthread_create()を呼び出すのは、共有ライブラリを使用する側で、

ライブラリからその呼び出し関数を指定するのは不可能では?

Windowsでは、DLLを使用している側がスレッドを作成すると、

システムがDllMain()をDLL_THREAD_ATTACHパラメータとともに呼び出し、

DLLはここで新規スレッドに対する初期化を行えるわけです。

nadachi
レギュラーアドバイザー

共有ライブラリのエントリポイントについて

> Windowsでは、DLLを使用している側がスレッドを

> 作成すると、システムがDllMain()

> をDLL_THREAD_ATTACHパラメータとともに

> 呼び出し、DLLはここで新規スレッドに対する

> 初期化を行えるわけです。

おっしゃるとおり、HP-UXのthreadモデルでは、

thread作成時にDLL全部の初期化ルーチンをもう一回呼ぶ、なんて機能はもともとありません。       pthread_create()のman pageを見ると、"新しいスレッドが作成されるときには、argを唯一のパラメータとする (3番目の引数で指定した) start_routine()を実行します。" とありますから、"thread作成時に何らかの動作をさせるには、pthread_create()で指定した関数の前のほうに書くしかありません。例えば、この中で、"isao"さんが例に書いたように、ライブラリごとのthread作成時に呼びたい/呼ばれたい関数の名前を決めておけば、shl_findsym()を使ってエントリアドレスを得て呼び出すことは可能でしょう。ですが、Windowsのthreadモデルと同じにはなりませんね。

nadachi
レギュラーアドバイザー

共有ライブラリのエントリポイントについて

 Added.

HP-UX 11.23の C言語のreferenceを読んでいたら

INIT と FINI という Pragmaがありました。

( ファイルは

/opt/ansic/html/C/guide/pragmas.htm

です。)

INIT Pragma

#pragma INIT "string"

初期化関数名を指定する。この関数は引数なしで、戻り値もなし。この関数は、プログラムの開始時か、または、共有ライブラリがロードされるときに実行される。

FINI Pragma

#pragma FINI "string"

この関数は、プログラムが停止、(libcの exit()を呼ぶ、または、main()からreturnする、または、_start関数からreturn する、または、これを含む共有ライブラリがメモリから unloadされる、時に実行されれる。

とありますね。DllMain()とは同じにはならないかも知れませんが、"ライブラリがロードされた時点で自動的に呼び出す"というのは出来そうですね。