差分表示
- 最後の更新で追加された行はこのように表示します。
- 最後の更新で削除された行は
このように表示します。
tnetio から、共有ライブラリをロードさせる方法と、処理関数について
* 概要
(x)inetd も、tcpserver も、最終的には、別の外部プログラムを exec します。
この時、プログラム内部で何が起こっているかを、簡単に列挙すると、
- 新規待ち受け
- fork() (自プロセスの分身(子プロセス)を作る)
- 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() の呼び出しタイミングは、原則的に外部プログラム実行タイミングと同じですが、以下の差異点があります。
- 標準入力、標準出力、標準エラー出力は、tnetsrv のものを継承している。
* セキュリティ
共有ライブラリをロードするタイミングは、ソケットへの bind を行い、setuid()、setgid() した後に行われます。従って、共有ライブラリをロードすると、_init()が
(存在していれば)呼び出されますが、それも、指定された uid/gid で呼び出されます。