wifidog认证源码分析Lighttpd1.4.20源码分析之插件系统(2)---插件的加载和初始化(1)
前面讲了lighttpd插件系统的接口,下面我们来看看插件是怎么加载 和初始化的。
lighttpd的插件是以动态链接库的形式存在的。在服务器启动的时候,在初始化阶段将所有插件都加载进来。在server.c中的main函数中,加载插件是调用plugins_load函数:
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1 if (plugins_load(srv))
{
log_error_write(srv, __FILE__, __LINE__, "s","loading plugins finally failed");
plugins_free(srv);
server_free(srv);
return -1;
}
请读者注意一下这个函数调用的位置。这个函数是在服务器的初始化阶段进行调用的,并且该函数就在这调用了一次,其他地方没有再被调用过。虽然插件是以动态链接库的形式存在,但这些库是在服务器启动阶段一次性加载完毕,如果想再增加插件,只能配置好配置文件后重新启动服务器。
下面看一看plugins_load函数的实现:
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 #ifdef LIGHTTPD_STATIC
int plugins_load(server * srv)
{
plugin *p;
#define PLUGIN_INIT(x)\
p = plugin_init(); \
if (x ## _plugin_init(p)) { \
log_error_write(srv, __FILE__, __LINE__, "ss",#x, "plugin init failed" ); \
plugin_free(p); \
return -1;\
}\
plugins_register(srv, p);
#include "plugin-static.h"
return 0;
}
#else
//动态链接
int plugins_load(server * srv)
{
plugin *p;
int (*init) (plugin * pl);
const char *error;
size_t i;
for (i = 0; i < srv->srvconf.modules->used; i++)
{
//获得动态链接库的名称。
data_string *d = (data_string *) srv->srvconf.modules->data[i];
char *modules = d->value->ptr;
//库所在目录
buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.modules_dir);
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
//拼接库的名称。
buffer_append_string(srv->tmp_buf, modules);
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so"));
p = plugin_init();
//linux调用函数dlopen加载动态库
if (NULL == ( p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW | RTLD_GLOBAL)))
{
log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:", srv->tmp_buf, dlerror());
plugin_free(p);
return -1;
}
//调用动态库中的XXX_plugin_init函数。
//XXX是库的名称
buffer_reset(srv->tmp_buf);
buffer_copy_string(srv->tmp_buf, modules);
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init"));
#if 1
//调用dlsym函数获得XXX_plugin_init函数的地址。
init = (int (*)(plugin *)) (intptr_t) dlsym(p->lib, srv->tmp_buf->ptr);
#else
//这句没有用
*(void **) (&init) = dlsym(p->lib, srv->tmp_buf->ptr);
#endif
if ((error = dlerror()) != NULL)
{
log_error_write(srv, __FILE__, __LINE__, "s", error);
plugin_free(p);
return -1;
}
//初始化插件
//在初始化的过程中,模块将自己所有的对外接口函数的入口地址都存入到p中。
if ((*init) (p))
{
log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin init failed");
plugin_free(p);
return -1;
}
#if 0
log_error_write(srv, __FILE__, __LINE__, "ss", modules,
"plugin loaded");
#endif
plugins_register(srv, p);
}
return 0;
}
#endif //end of #ifdef 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模块中:
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 int mod_cgi_plugin_init(plugin * p)
{
p->version = LIGHTTPD_VERSION_ID;
p->name = buffer_init_string("cgi");
p->connection_reset = cgi_connection_close_callback;
p->handle_subrequest_start = cgi_is_handled;
p->handle_subrequest = mod_cgi_handle_subrequest;
#if 0
p->handle_fdevent = cgi_handle_fdevent;
#endif
p->handle_trigger = cgi_trigger;
p->init = mod_cgi_init;
p->cleanup = mod_cgi_free;
p->set_defaults = mod_fastcgi_set_defaults;
p->data = NULL;
return 0;
}
plugins_register函数是将plugin结构体的指针存放在server结构体的plugins数组中。
插件加载完毕之后,main函数有处理了一些初始化的工作,然后,调用plugins_call_init函数对所有插件进行初始化:
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1 if (HANDLER_GO_ON != plugins_call_init(srv))
{
log_error_write(srv, __FILE__, __LINE__, "s",
"Initialization of plugins failed. Going down.");
plugins_free(srv);
network_close(srv);
server_free(srv);
return -1;
}
本文章由 http://www.wifidog.pro/2015/04/17/wifidog%E8%AE%A4%E8%AF%81%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90lighttpd%E5%8A%A0%E8%BD%BD%E5%92%8C%E5%88%9D%E5%A7%8B%E5%8C%96-1.html 整理编辑,转载请注明出处