专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »编程综合 » lighttpd1.4.18:Lighttpd1.4.20源码分析的插件系统(2)---插件的加载和初始化 »正文

lighttpd1.4.18:Lighttpd1.4.20源码分析的插件系统(2)---插件的加载和初始化

来源: 发布时间:星期三, 2010年3月17日 浏览:0次 评论:0
  前面讲了lighttpd插件系统接口下面我们来看看插件是如何加载 和

  lighttpd插件是以动态链接库形式存在在服务器启动时候化阶段将所有插件都加载进来在server.c中加载插件是plugins_load:

1  (plugins_load(srv))
2 {
3     log_error_write(srv, __FILE__, __LINE__, "s","loading plugins finally failed");
4     plugins_free(srv);
5     server_free(srv);
6      -1;
7 }


  请读者注意下这个位置这个是在服务器化阶段进行并且该就在这其他地方没有再被虽然插件是以动态链接库形式存在但这些库是在服务器启动阶段次性加载完毕如果想再增加插件只能配置好配置文件后重新启动服务器

  下面看看plugins_load实现:

 1 #def LIGHTTPD_STATIC
 2   plugins_load(server * srv)
 3 {
 4     plugin *p;
 5  # PLUGIN_INIT(x)\
 6     p = plugin_init; \
 7      (x ## _plugin_init(p)) { \
 8         log_error_write(srv, __FILE__, __LINE__, "ss",#x, "plugin init failed" ); \
 9         plugin_free(p); \
10          -1;\
11     }\
12     plugins_register(srv, p);
13 # "plugin-.h"
14      0;
15 }
16  #
17  //动态链接
18   plugins_load(server * srv)
19 {
20     plugin *p;
21      (*init) (plugin * pl);
22     const char *error;
23     size_t i;
24     for (i = 0; i < srv->srvconf.modules->used; i)
25     {
26         //获得动态链接库名称
27          data_ *d = (data_ *) srv->srvconf.modules->data[i];
28         char *modules = d->value->ptr;
29         //库所在目录
30          buffer_copy__buffer(srv->tmp_buf, srv->srvconf.modules_dir);
31 
32         buffer_append__len(srv->tmp_buf,     CONST_STR_LEN("/"));
33         //拼接库名称
34          buffer_append_(srv->tmp_buf, modules);
35         buffer_append__len(srv->tmp_buf, CONST_STR_LEN(".so"));
36         p = plugin_init;
37                 //linuxdlopen加载动态库
38           (NULL  ( p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW | RTLD_GLOBAL)))
39         {
40             log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen failed for:", srv->tmp_buf, dlerror);
41             plugin_free(p);
42              -1;
43         }
44         //动态库中XXX_plugin_init
45         //XXX是库名称
46          buffer_re(srv->tmp_buf);
47         buffer_copy_(srv->tmp_buf, modules);
48         buffer_append__len(srv->tmp_buf, CONST_STR_LEN("_plugin_init"));
49  # 1
50         //dlsym获得XXX_plugin_init地址
51          init = ( (*)(plugin *)) (ptr_t) dlsym(p->lib, srv->tmp_buf->ptr);
52  #
53         //这句没有用
54          *(void **) (&init) = dlsym(p->lib, srv->tmp_buf->ptr);
55  #end
56          ((error = dlerror) != NULL)
57         {
58             log_error_write(srv, __FILE__, __LINE__, "s", error);
59             plugin_free(p);
60              -1;
61         }
62         //化插件
63     //在过程中模块将自己所有对外接口入口地址都存入到p中
64           ((*init) (p))
65         {
66             log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin init failed");
67             plugin_free(p);
68              -1;
69         }
70  # 0
71         log_error_write(srv, __FILE__, __LINE__, "ss", modules,
72                         "plugin loaded");
73  #end
74         plugins_register(srv, p);
75     }
76      0;
77 }
78  #end //end of #def LIGHTTPD_STATIC


  上面中删除了处理在windows下加载动态链接库部分有兴趣读者可以自行查看源代码下面全部讨论在linux下实现

  这个作者编写了两个版本从宏LIGHTTPD_STATIC可以看出作者貌似是想提供个加载静态链接库版本但是该版本并没有什么实质性实现(在最新版本中(1.4.26)依然没有实现)我们主要来看看后面动态链接库版本

  加载过程如下:

  (1)从配置文件中读取到动态链接库所在文件夹和动态库名称

  (2)创建个plugin结构体例子

  (3)dlopen加载动态库在plugin结构体中保存返回句柄

  (4)通过dlsym获得XXXXXX_plugin_init地址其中XXXXXX是配置文件中定义这个插件内容

  (5)XXXXXX_plugin_init

  (6)plugins_register注册插件

  XXXXXX_plugin_init在插件中定义对这个插件进行其中最重要部分就是对plugin结构体中那系列指针进行赋值mod_cgi模块中:

 1  mod_cgi_plugin_init(plugin * p)
 2 {
 3     p->version = LIGHTTPD_VERSION_ID;
 4     p->name = buffer_init_("cgi");
 5     p->connection_re = cgi_connection_close_callback;
 6     p->handle_subrequest_start = cgi_is_handled;
 7     p->handle_subrequest = mod_cgi_handle_subrequest;
 8 # 0
 9     p->handle_fdevent = cgi_handle_fdevent;
10 #end
11     p->handle_trigger = cgi_trigger;
12     p->init = mod_cgi_init;
13     p->cleanup = mod_cgi_free;
14     p->_defaults = mod_fastcgi__defaults;
15     p->data = NULL;
16      0;
17 }


  plugins_register是将plugin结构体指针存放在server结构体plugins

  插件加载完毕的后有处理了工作然后plugins_call_init对所有插件进行化:

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


  plugins_call_init在plugin.c文件中:

 1 handler_t plugins_call_init(server * srv)
 2 {
 3     size_t i;
 4     plugin **ps;
 5     ps = srv->plugins.ptr;
 6     /*
 7      * fill slots 
 8      */
 9     srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, (ps));
10     for (i = 0; i < srv->plugins.used; i)
11     {
12         size_t j;
13         /*
14          * check which calls are supported 
15          */
16         plugin *p = ps[i];
17     /**
18      * 对所有plugin进行登记造册这个宏在后文中着重讲解
19      */
20 # PLUGIN_TO_SLOT(x, y) \
21      (p->y) { \
22         plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \
23          (!slot) { \
24             slot = calloc(srv->plugins.used, (*slot));\
25             ((plugin ***)(srv->plugin_slots))[x] = slot; \
26         } \
27         for (j = 0; j < srv->plugins.used; j) { \
28              (slot[j]) continue;\
29             slot[j] = p;\
30             ;\
31         }\
32     }
33         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean);
34         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw);
35         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done);
36         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,  handle_connection_close);
37         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger);
38         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup);
39         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest);
40         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start);
41         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist);
42         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot);
43         PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical);
44         PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_re);
45         PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup);
46         PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, _defaults);
47 #undef PLUGIN_TO_SLOT
48         //对插件进行
49          (p->init)
50         {
51              (NULL  (p->data = p->init))
52             {
53                 log_error_write(srv, __FILE__, __LINE__, "sb", "plugin-init failed for module", p->name);
54                  HANDLER_ERROR;
55             }
56             /*
57              * used for con->mode,DIRECT0,plugins above that 
58              */
59             ((plugin_data *) (p->data))->id = i + 1;
60             //这里检测插件版本是否和当前服务器版本相同
61             //这里保证如果以后插件接口发生了改变不会造成服务器崩溃
62              (p->version != LIGHTTPD_VERSION_ID)
63             {
64                 log_error_write(srv, __FILE__, __LINE__, "sb","plugin-version doesn't match lighttpd-version for", p->name);
65                  HANDLER_ERROR;
66             }
67         } 
68         
69         {
70             p->data = NULL;
71         }
72     }
73      HANDLER_GO_ON;
74 }


  整个这个宏是重点:

 1 # PLUGIN_TO_SLOT(x, y) \
 2      (p->y) { \
 3         plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \
 4          (!slot) { \
 5             slot = calloc(srv->plugins.used, (*slot));\
 6             ((plugin ***)(srv->plugin_slots))[x] = slot; \
 7         } \
 8         for (j = 0; j < srv->plugins.used; j) { \
 9              (slot[j]) continue;\
10             slot[j] = p;\
11             ;\
12         }\
13     }


  在结构体server中plugin_slots是个void指针在这个宏中可以看到plugin_slots被转换成了plugin结构体 3级指针朝前看:

  srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, (ps));

  plugin_slots是个存放ps类型数据长度为PLUGIN_FUNC_SIZEOF PLUGIN_FUNC_SIZEOF在后面介绍说明ps类型是plugin结构体 2级指针在上面宏中我们看到plugin_slots是随后分支中可以看到plugin_slots元素也是因此plugin_slots是个 2维元素是plugin 结构体指针并且plugin_slots是动态创建

  下面在来看PLUGIN_FUNC_SIZEOF它定义在下面枚举结构中:

 1 typedef enum 
 2 {
 3     PLUGIN_FUNC_UNSET,
 4     PLUGIN_FUNC_HANDLE_URI_CLEAN,
 5     PLUGIN_FUNC_HANDLE_URI_RAW,
 6     PLUGIN_FUNC_HANDLE_REQUEST_DONE,
 7     PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE,
 8     PLUGIN_FUNC_HANDLE_TRIGGER,
 9     PLUGIN_FUNC_HANDLE_SIGHUP,
10     PLUGIN_FUNC_HANDLE_SUBREQUEST,
11     PLUGIN_FUNC_HANDLE_SUBREQUEST_START,
12     PLUGIN_FUNC_HANDLE_JOBLIST,
13     PLUGIN_FUNC_HANDLE_DOCROOT,
14     PLUGIN_FUNC_HANDLE_PHYSICAL,
15     PLUGIN_FUNC_CONNECTION_RESET,
16     PLUGIN_FUNC_INIT,
17     PLUGIN_FUNC_CLEANUP,
18     PLUGIN_FUNC_SET_DEFAULTS,
19 
20     PLUGIN_FUNC_SIZEOF
21 } plugin_t;


  从枚举结构名字可以看出这个枚举类型定义了插件功能类型对应着plugin结构体中那些指针而最后个量PLUGIN_FUNC_SIZEOF根据枚举类型特点正好是上面所定义类型数量这是个很常用窍门技巧这样可以保证在增加类型时候保证中可以得到正确类型数量而不要去改动那些需要类型数量代码

  接着回到上面这个宏有两个参数有后面使用可以看出个参数x是枚举类型plugin_t第 2个参数x对应在plugin结构体中指针名称因此上面作用就是:根据参数x所指定插件功能类型判断插件p中是否含有功能x也就是指针p->y是否非 NULL如果包含功能x则将指针p添加到plugin_slots第x行中内层语句是为了判断plugin_slots第x行是否存在不存在则创建的for循环是为了将p添加到plugin_slots第x行末尾

  例如插件*p1, *p2, *p3,在执行完后面那些宏的后会形成个如下表:



  查看原图(大图)

  从表中可以看出插件p1包含所有功能也就是实现了plugin结构体中指针对应所有插件p2不包含功能 HANDLE_SUBREQUESTHANDLE_SUBREQUEST_START和HANDLE_REQUEST_DONE功能插件p3中不包含

  HANDLE_TRIGGERHANDLE_SIGHUP和CONNECTION_RESET功能当然这仅仅是举个例子

  上面例子中表就是plugins_slots这个宏和后面可以看成是给所有插件“登记造册”通过plugins_slots可以快速确定某个功能都有那些插件实现了这方便后面插件

  完成这些宏测试插件是否定义了init如果定义了则这里init和前面加载 XXX_plugin_initXXX_plugin_init化plugin结构体核心工作是对plugin结构体中指针进行赋值而init则是化这个插件对应plugin_data结构体分配数据空间化成员变量并返回其指针

  如:mod_cgi.cinit定义为

1 INIT_FUNC(mod_cgi_init)
2 {
3     plugin_data *p;
4     p = calloc(1, (*p));
5     assert(p);
6     p->tmp_buf = buffer_init;
7     p->parse_response = buffer_init;
8      p;
9 }


  返回指针存放在plugin结构体中data成员中然后对data中id进行赋值接着检查插件版本是否和当前服务器版本相同

  如果插件没有定义init则data赋值NULL

  至此插件加载和化工作全部完成了下面整理总结下整个加载和过程:

  (1)根据配置文件从相应目录中加载插件动态连接库

  (2)获得插件动态库中XXX_plugin_init入口地址并对plugin结构体进行赋值

  (3)在server结构体中注册插件

  (4)plugins_call_init化插件

  (5)通过上面那个宏及后面系列将插件登记造册记录在server结构体plugins_slots成员中plugins_slot是个 2维成员是plugin结构体指针



  (6)最后插件init化各自plugin_data结构体

  下篇中将介绍下plugin.c中宏PLUGIN_TO_SLOT



0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: