lighttpd1.4.18:Lighttpd1.4.20源码分析的插件系统(3)---PLUGIN_TO_SLOT宏

  前面讲了lighttpd插件系统加载和篇中将介绍下plugin.c中宏PLUGIN_TO_SLOT

  在将 PLUGIN_TO_SLOT宏的前我们先来看看lighttpd中插件系统对外接口这个接口所对“外”指是lighttpd服务器前面已经提到在运行过程中lighttpd不知道所加载插件都是干什么用只知道这些插件所实现接口也就是在plugin结构体中那些指针有哪些对于某个插件是NULL哪些是具体地址

  既然lighttpd只知道这些那么它又是怎样这些插件呢?

  答案就在plugin.h文件中下面系列声明:

 1 handler_t plugins_call_handle_uri_raw(server * srv, connection * con);
 2 handler_t plugins_call_handle_uri_clean(server * srv,connection * con);
 3 handler_t plugins_call_handle_subrequest_start(server * srv,connection * con);
 4 handler_t plugins_call_handle_subrequest(server * srv,connection * con);
 5 handler_t plugins_call_handle_request_done(server * srv,connection * con);
 6 handler_t plugins_call_handle_docroot(server * srv,connection * con);
 7 handler_t plugins_call_handle_physical(server * srv,connection * con);
 8 handler_t plugins_call_handle_connection_close(server * srv,connection * con);
 9 handler_t plugins_call_handle_joblist(server * srv,connection * con);
10 handler_t plugins_call_connection_re(server * srv,connection * con);
11 handler_t plugins_call_handle_trigger(server * srv);
12 handler_t plugins_call_handle_sighup(server * srv);
13 handler_t plugins_call_init(server * srv);
14 handler_t plugins_call__defaults(server * srv);
15 handler_t plugins_call_cleanup(server * srv);


  这些就是插件系统对外接口在运行过程中lighttpd靠上面这些插件比如:在server.c了plugins_call__defaults:

1  (HANDLER_GO_ON != plugins_call__defaults(srv))
2   {
3     log_error_write(srv, __FILE__, __LINE__, "s",
4         "Configuration of plugins failed. Going down.");
5     plugins_free(srv);
6     network_close(srv);
7     server_free(srv);
8      -1;
9   }


  如果你使用ctags+vim看代码当在这些处想跳转到定义时发现ctags没有找到这些定义难道这些没有实现?这显然是不会其实这正是由于本文重点───PLUGIN_TO_SLOT宏造成

  打开plugin.c文件会发现这几行代码:

 1 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)
 2 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw)
 3 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE,handle_request_done)
 4 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,handle_connection_close)
 5 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest)
 6 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START,handle_subrequest_start)
 7 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist)
 8 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot)
 9 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical)
10 PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_re)
11 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger)
12 PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup)
13 PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup)
14 PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, _defaults)


  再看看PLUGIN_SLOT宏前两行:

1 # PLUGIN_TO_SLOT(x, y) \
2   handler_t plugins_call_##y(server *srv, connection *con) {\


  这下明白了吧上面那些是由这些宏模板化生成由于这些代码具有很高相似度(仅仅是插件区别)通过宏模板进行生成可以节约大量代码同时又不容易出错这类似于C模板注:C语言预处理器运算符##为宏扩展提供了种连接实际参数手段如果替换文本中参数和##相邻则改参数将被实际参数替换##和前后空白符将被删除并对替换后结果重新扫描(摘自:C语言设计 K&R)在这里plugins_call_和实参y拼接成

  下面我们着重分析下PLUGIN_SLOT宏内容:

 1 # PLUGIN_TO_SLOT(x, y) \
 2   handler_t plugins_call_##y(server *srv, connection *con) {\
 3     plugin **slot;\
 4     size_t j;\
 5      (!srv->plugin_slots)  HANDLER_GO_ON;\
 6     slot = ((plugin ***)(srv->plugin_slots))[x];\
 7      (!slot)  HANDLER_GO_ON;\
 8     for (j = 0; j < srv->plugins.used && slot[j]; j) { \
 9       plugin *p = slot[j];\
10       handler_t r;\
11       switch(r = p->y(srv, con, p->data)) {\
12        HANDLER_GO_ON:\
13         ;\
14        HANDLER_FINISHED:\
15        HANDLER_COMEBACK:\
16        HANDLER_WAIT_FOR_EVENT:\
17        HANDLER_WAIT_FOR_FD:\
18        HANDLER_ERROR:\
19          r;\
20       default:\
21         log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\
22          HANDLER_ERROR;\
23       }\
24     }\
25      HANDLER_GO_ON;\
26   }


  根据后面可以看出参数x是plugin_t枚举类型y是plugin结构体中指针成名名字在宏x和y是相对应

  PLUGIN_SLOT宏首先通过参数y拼接出如:这个宏

  PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean)拼接得到名为plugins_call_handle_uri_clean加上参数和返回值正好是 plugin.h中handler_t plugins_call_handle_uri_clean(server * srv, connection * con)其他以此类推

   这条语句slot = ((plugin ***)(srv->plugin_slots))[x];通过宏参数x得到plugin_slots第x列plugin_slots结构在前面文章中已经讲解过了不熟悉读者可以再回头看看这列中包含所有具有参数x所对应功能插件指针也就是plugin结构体成员变量y不为NULL所有plugin例子指针接着在for循环中这些插件y就是这句:switch(r = p->y(srv, con, p->data))后面就是些返回值和处理了

   读者也许早就发现在plugin.c文件中有两个PLUGIN_SLOT宏猛地看没有任何差别确实着两个宏基本上都只有点区别:第 2个宏switch语句中y参数少了个con:switch(r = p->y(srv, p->data))这是他们唯差别读者可以看看plugin结构体中指针有 4个是两个参数(server * srv, void *p_d)其他都是 3个参数(server * srv, connection * con, void *p_d)

   在这里有个很有意思问题我们注意到在所有plugins_call_XXX由于都是通过上面PLUGIN_SLOT宏生成那么这些在被lighttpd进行时候无论来请求想要干什么lighttpd都会逐所有插件对应这就有个问题了:如果所个插件所具有功能不是这个连接所想要功能这不就出错了么?但是反过来想想既然要隐藏插件所有细节那么lighttpd也就无从知道哪些插件是干什么因此对和个连接lighttpd也就不会知道使用哪个插件进行处理因此这样做也是没办法事情虽然这样会影响服务器效率毕竟每次都了很多无用但却有助于服务器扩展效率换扩展性关键要把握个度



   那么lighttpd无法确定连接该用哪个插件来处理那么插件自己就必须知道哪些连接是自己该处理这涉及到连接细节处理我们先暂且放从下篇开始将讲解下fd event系统在分析具体连接处理时在讲解这个问题

   下介绍lighttpd中fdevents结构体和fd eve



Tags:  4.20是什么星座 lighttpd1.4.18

延伸阅读

最新评论

发表评论