首页 »编程综合 » 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_SUBREQUEST HANDLE_SUBREQUEST_START和HANDLE_REQUEST_DONE功能 插件p3中不包含 HANDLE_TRIGGER HANDLE_SIGHUP和CONNECTION_RESET功能 当然 这仅仅是举个例子
上面 例子中 表就是plugins_slots 这个宏和后面 宏 可以看成是给所有 插件“登记造册” 通过plugins_slots 可以快速 确定某个功能都有那些插件实现了 这方便后面 插件 完成这些宏 后 化 测试插件是否定义了init 如果定义了则 的 这里 init 和前面加载 中 XXX_plugin_init 不 样 XXX_plugin_init 化 是 化plugin结构体 核心工作是对plugin结构体中 指针进行赋值 而init 则是 化这个插件对应 plugin_data结构体 分配数据空间 化成员变量并返回其指针 如:mod_cgi.c init 定义为 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
相关文章
读者评论
发表评论
|
|