新規作成 | 編集(管理者用) | 差分 | 検索 | tnetio | 一覧 | 更新履歴 | RSS SourceForge.jp

50SO_LOAD - tnetio から、共有ライブラリをロードさせる方法と、処理関数について

概要

(x)inetd も、tcpserver も、最終的には、別の外部プログラムを exec します。 この時、プログラム内部で何が起こっているかを、簡単に列挙すると、

上記の様な事が起こっています。これは、tnetsrv でも同じです。

ネットワークサービスを並行して連続稼動させるためには、fork() に よる子プロセスの生成は、TCP の場合、必要になりますが、その都度 exec するので、OS に負担をかけ、初期のレスポンスが(若干ではあるものの)悪く なります(もちろん、人間にしてみれば、ほんの一瞬ですが...)。

 

その対処法として、子プロセス生成時に、fork() を使用せずに、vfork() を使用する方法があります。

これは、vfork() した親プロセスは停止させておき、子プロセス側が必ず exec する事で、fork() にかかる負荷を低減させるものですが、ネットワーク サービスの場合、親は親で、子プロセスの生成を行ったならば、直ちに次の 待ち受け動作に入らせたい所です。それに、vfork() を使って低減できるのは、 子プロセスの生成にかかるOSコストだけで、vrofk() を使っても、exec にかかる コストは減らない(はず)です。 (間違っていたらご指摘下さい)

 

UNIX の場合、「子プロセスを生成するOSコスト」と、「別プログラムを実行するOSコスト」は、それぞれ分かれています。

 

ネットワークサービスの運用としては、大抵、1ポート1サービス(1機能)で運用 されているケースが多いと思います(あるポートで、telnet や ssh が 混在している事はあまり無いでしょう)。と言う事は、tnetsrv が動い ている時は、基本的にそのプログラムが変わる事は少ないとも言えます。

 

そこで、tnetsrv では、ダイナミックリンク機能を使用し、待ち受けを行う前に、 まず共有ライブラリをロードし、新規接続の際は、外部プログラムを exec する のではなく、すでにロードした共有ライブラリのある関数を呼び出す事で、exec にかかるコストを減らせるようにします。

注意点

SAMPLE

inetd は、daytime (ホストの日付時刻を返す)サービスは、internal、すなわち 内部ですでに持っている状態です。

それとほぼ同じことが出来る共有ライブラリを、tnetio_daytime.so と言う名前で 作成してみます。

共有ライブラリ tnetio_daytime.so のソースは、以下の様になります。

 /* tnetio_daytime.c - inetd の、daytime に相当するサービスライブラリ */
 
 #include <stdio.h>
 #include <unistd.h>
 #include <time.h>
 #include <string.h>
 
 /* 本ライブラリがロードされた時に呼び出される */
 void _init(void)
 {
 }
 
 /* 本ライブラリがアンロードされた時に呼び出される */
 void _fini(void)
 {
 }
 
 /* tnetsrv は、この関数を呼び出す */
 int tnetio_run(int in_fd,int out_fd,int argc,char **argv)
 {
         time_t tim = 0;
         char *tim_str = NULL; 
 
         tim = time(NULL);       /* 現在時間を取得 */
         tim_str = ctime(&tim);  /* 現在時間を文字列に変換 */
         write(out_fd,tim_str,strlen(tim_str));  /* ネットワークに書き出す */
 
         return 0;       /* 終了する */
 }

このソースをコンパイルします。

 $ gcc -shared -fPIC -nostdlib -o tnetio_daytime.so tnetio_daytime.c

作成した tnetio_daytime.so を呼び出す様に、tnetsrv を起動します。ポート 10123 番で待ち受けます。

 $ tnetsrv -s 10123 ./tnetio_daytime.so

別の端末から、telnet してみます。

 $ telnet localhost 10123
 Trying 127.0.0.1...
 Connected to localhost.
 Escape character is '^]'.
 Thu May 15 14:40:12 2003 <---- ※
 Connection closed by foreign host.

上記、※の部分が、共有ライブラリのtnetio_run()実行結果です。

関数仕様

tnetsrv は、共有ライブラリをロードする(-s オプション指定時)場合、関数名 tnetio_run() を探し、新規接続が行われた場合に、tnetio_run() を呼び出します。  

tnetio_run() の関数仕様は、以下の通りです。

 int tnetio_run(int in_fd,int out_fd,int argc,char **argv)

in_fd
ネットワーク入力用ファイルディスクリプタです。
out_fd
ネットワーク出力用ファイルディスクリプタです。
argc
後に続く argv の有効数です。
argv
引数が文字列で格納されます。argv[0] には、共有ライブラリの名前そのものが格納されています。

 

tnetio_run() 終了時、in_fd,out_fd が示すソケットは、必ずしもクローズする必要は ありません(tnetsrv が、tnetio_run() から帰ってきた後にクローズします)。

tnetio_run() の呼び出しタイミングは、原則的に外部プログラム実行タイミングと同じですが、以下の差異点があります。

セキュリティ

共有ライブラリをロードするタイミングは、ソケットへの bind を行い、setuid()、setgid() した後に行われます。従って、共有ライブラリをロードすると、_init()が (存在していれば)呼び出されますが、それも、指定された uid/gid で呼び出されます。