Lightpd 1.4.20ソース分析のプラグインシステム(3)---PLUGIN_TOSLOTマクロ


前にlightpdプラグインシステムのロードと初期化について説明しましたが、ここではplugin.cのマクロPLUGIN_を紹介します。TOSLOTPLUGIN_をTOSLOTマクロの前に、私達はまずlightpdのプラグインシステムの対外インターフェースを見にきます。このインターフェースの対する「外」とはライトpdサーバを意味します。前に述べたように、実行中のlightpdは、ロードされたプラグインが何をするかは分かりません。これらのプラグインによって実現されるインターフェース、すなわちplugin構造体の中の関数ポインタは、どのようなプラグインがNULLですか?具体的な関数アドレスですか?lighttpdはこれらしか知らないのですが、これらのプラグインはどうやって呼び出されますか?
答えはplugin.hファイルの次の関数宣言にあります。
 handler_t plugins_call_handle_uri_raw(server * srv, connection * con);
 handler_t plugins_call_handle_uri_clean(server * srv,connection * con);
 handler_t plugins_call_handle_subrequest_start(server * srv,connection * con);
 handler_t plugins_call_handle_subrequest(server * srv,connection * con);
 handler_t plugins_call_handle_request_done(server * srv,connection * con);
 handler_t plugins_call_handle_docroot(server * srv,connection * con);
 handler_t plugins_call_handle_physical(server * srv,connection * con);
 handler_t plugins_call_handle_connection_close(server * srv,connection * con);
 handler_t plugins_call_handle_joblist(server * srv,connection * con);
 handler_t plugins_call_connection_reset(server * srv,connection * con);
 handler_t plugins_call_handle_trigger(server * srv);
 handler_t plugins_call_handle_sighup(server * srv);
 handler_t plugins_call_init(server * srv);
 handler_t plugins_call_set_defaults(server * srv);
 handler_t plugins_call_cleanup(server * srv);
これらの関数はプラグインシステムの外部インターフェースです。実行中、lightpdは上のこれらの関数を呼び出してプラグインを起動します。例えば、server.cのmain関数で、plugins(u)を呼び出しました。コールsetdefaults関数:
if (HANDLER_GO_ON != plugins_call_set_defaults(srv))
     {
         log_error_write(srv, __FILE__, __LINE__, "s",
                 "Configuration of plugins failed. Going down.");
         plugins_free(srv);
         network_close(srv);
         server_free(srv);
         return -1;
     }
ctags+vimを使ってコードを見ると、これらの関数の呼び出しで定義にジャンプしたい場合、ctagsはこれらの関数の定義が見つかりませんでした。これらの関数は実装されていませんか?これは明らかにできません。実は、これはまさに本文のポイントです。TOSLOTマクロによるものです
plugin.cファイルを開くと、これらの行のコードが見つかります。
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE,handle_request_done)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,handle_connection_close)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START,handle_subrequest_start)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup)
 PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults)
PLUGIN_を見てみますSLOTマクロの最初の2行:
#define PLUGIN_TO_SLOT(x, y) \
handler_t plugins_call_##y(server *srv, connection *con) {\
これでわかったでしょう。上記の関数は、これらのマクロからテンプレート化を呼び出して生成されます。これらの関数のコードは非常に類似度が高いため、マクロテンプレートを通じて生成され、多くのコードを節約できます。これはC++のテンプレートと似ています。注:C言語プリプロセッサ演算子ヒドラは、マクロ拡張のために実際のパラメータを接続する手段を提供します。置換テキスト中のパラメータが萼と隣接していると、修正パラメータは実際のパラメータに置き換えられ、萼〹と前後の空白符は削除され、置換後の結果を再スキャンします。(抜粋:C言語プログラミングK&R)ここで、plugins_コールと参照してください。PLUGIN_を重点的に分析します。SLOTマクロの内容:
#define PLUGIN_TO_SLOT(x, y) \
     handler_t plugins_call_##y(server *srv, connection *con) {\
         plugin **slot;\
         size_t j;\
         if (!srv->plugin_slots) return HANDLER_GO_ON;\
         slot = ((plugin ***)(srv->plugin_slots))[x];\
         if (!slot) return HANDLER_GO_ON;\
         for (j = 0; j < srv->plugins.used && slot[j]; j++) { \
             plugin *p = slot[j];\
             handler_t r;\
             switch(r = p->y(srv, con, p->data)) {\
             case HANDLER_GO_ON:\
                 break;\
             case HANDLER_FINISHED:\
             case HANDLER_COMEBACK:\
             case HANDLER_WAIT_FOR_EVENT:\
             case HANDLER_WAIT_FOR_FD:\
             case HANDLER_ERROR:\
                 return r;\
             default:\
                 log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\
                 return HANDLER_ERROR;\
             }\
         }\
         return HANDLER_GO_ON;\
     }
後のマクロ呼び出しにより、パラメータxがplugin_であることが分かります。t列挙の種類、yはplugin構造体の関数ポインタが有名な名前です。マクロ呼び出しでは、xとyは対応する。
PLUGIN_SLOTマクロは、まずパラメータyで関数名をつづり合わせます。このマクロ呼び出し
PLUGIN_TOSLOT(PLUGINKUFUNK)は、スティッチした関数の名前がplugins(u)です。コールハンドルウニ畑cleanは、パラメータと戻り値を加えて、ちょうどplugin.hの関数handler_です。t plugins_コールハンドルウニ畑clean。その他はこれを類推する。
    この文はslaot=(plugin*)[x];マクロパラメータxでplugin_を得ます。slaotsのx列目。プラグニン.slaotsの仕組みは前の文章で説明されました。よく知らない読者はまた振り返ってみてもいいです。この列には、パラメータxに対応する機能を持つすべてのプラグインのポインタが含まれています。つまり、plugin構造体のメンバ変数yはNULLの全plugin例の指針ではない。続いて、forサイクルでは、これらのプラグインのy関数を呼び出します。これです。後は戻り値とエラー処理です。
    読者はとっくにplugin.cnファイルの中に二つのPLUGIN_があることを発見したかもしれません。SLOTマクロ。猛然と見てもなんら違いがない。確かに、二つのマクロは基本的に同じです。一つだけ違っています。二つ目のマクロのswitch文でy関数を呼び出すと、パラメータが一つ足りなくなります。これは彼らの唯一の違いです。読者はplugin構造体の関数ポインタを見ることができます。四つは二つのパラメータです。他は三つのパラメータです。
    ここに面白い問題があります。私たちはすべてのplugins_に注目しています。コールXXX関数では、上のPLUGIN_を通過します。SLOTマクロが生成されました。これらの関数は、lightpdによって呼び出されると、来た要求が何をしたいのかに関わらず、lighttpdはすべてのプラグインに対応する関数をそれぞれ呼び出します。これは問題があります。呼出された最初のプラグインが持っている機能がこの接続に必要な機能ではないなら、エラーではないですか?しかし、逆に考えると、プラグインの詳細をすべて隠すためには、lightpdはどのプラグインが何をしているのか分かりません。したがって、一つの接続に対しては、lightpdもどのプラグインを使って処理するか分かりません。だから、こうするのも仕方がないです。このようにサーバーの効率に影響を与えますが、毎回無駄な関数をたくさん呼び出します。しかし、サーバーの拡張に役立ちます。効率は拡張性を変えて、肝心な点は度を把握します。
    lighttpdはどのプラグインで接続すればいいのか確認できません。プラグイン自体はどの接続が自分の処理すべきかを知る必要があります。これは接続の詳細処理に関しては、しばらくおいておきます。次のページから、fdイベントシステムについて説明します。具体的な接続の処理を分析する時、この問題を説明します。