wifidog源码分析Lighttpd1.4.20源码分析之插件系统(3)---PLUGIN_TO_SLOT宏

前面讲了lighttpd插件系统的加载和初始化,这一篇中,将介绍一下plugin.c中的宏PLUGIN_TO_SLOT。
在将PLUGIN_TO_SLOT宏之前,我们先来看看lighttpd中插件系统的对外接口。这个接口所对的“外”指的是lighttpd服务器。前面已经提到,在运行的过程中,lighttpd不知道所加载的插件都是干什么用的,只知道这些插件所实现的接口,也就是在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);

这些函数就是插件系统对外的接口。在运行过程中,lighttpd靠调用上面的这些函数调用插件。比如:在server.c的main函数中,就调用了plugins_call_set_defaults函数:

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没有找到这些函数的定义。难道这些函数没有实现?这显然是不会的。其实,这正是由于本文的重点───PLUGIN_TO_SLOT宏造成的。
打开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宏的前两行:

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

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

宏 PLUGIN_TO_SLOT

根据后面的宏调用可以看出,参数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宏。猛地一看没有任何差别。确实,着两个宏基本上都一样,只有一点不同:第二个宏的switch语句中调用y函数时,参数少了一个con:switch(r = p->y(srv, p->data))。这是他们唯一的差别。读者可以看看plugin结构体中的函数指针,有四个是两个参数的(server * srv, void *p_d),其他都是三个参数(server * srv, connection * con, void *p_d)。
在这里有一个很有意思的问题。我们注意到,在所有plugins_call_XXX函数中,由于都是通过上面的PLUGIN_SLOT宏生成的。那么,这些函数在被lighttpd进行调用的时候,无论来的请求想要干什么,lighttpd都会逐一调用所有插件对应的函数。这就有一个问题了:如果所调用的第一个插件所具有的功能不是这个连接所想要的功能,这不就出错了么?但是反过来想想,既然要隐藏插件的所有细节,那么lighttpd也就无从知道哪些插件是干什么的。因此,对与一个连接,lighttpd也就不会知道使用哪个插件进行处理。因此,这样做也是没办法的事情。虽然这样会影响服务器的效率,毕竟每次都调用了很多无用的函数,但却有助于服务器的扩展。效率换扩展性,关键要把握一个度。
那么lighttpd无法确定连接该用哪个插件来处理,那么插件自己就必须知道哪些连接是自己该处理的。这涉及到连接的细节处理,我们先暂且放一放。从下一篇开始,将讲解一下fd event系统,在分析具体的连接的处理时,在讲解这个问题。
下一篇,介绍lighttpd中的fdevents结构体和fd eve

本文章由http://www.wifidog.pro/2015/04/17/wifidog%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90lighttpd%E6%8F%92%E4%BB%B6%E5%AE%8F%E5%AE%9A%E4%B9%89.html 整理编辑,转载请注明出处

时间: 2024-10-10 00:11:18

wifidog源码分析Lighttpd1.4.20源码分析之插件系统(3)---PLUGIN_TO_SLOT宏的相关文章

wifidog源码分析Lighttpd1.4.20源码分析之fdevent系统(1)---fdevents结构体和fdevent系统对外接口

前面讲了lighttpd的插件系统,这一篇将看一看lighttpd中的fdevent系统.fdevent系统主要是处理各种IO事件,在web服务器中,主要就是向socket写数据和从socket读数据.通常,web服务器是IO密集型程序,这就要求在数据的读写上,web服务器必须能够具有很好的性能,不会因为某个socket的阻塞而致使其他socket也被阻塞,否则会大大降低服务器的性能.因此,大部分的web服务器都采用非阻塞IO进行数据的读写.lighttpd通过fdevent系统,采用类似OO中

wifidog源码分析Lighttpd1.4.20源码分析之fdevent系统(3) -----使用

接着上文介绍的函数fdevent_linux_sysepoll_event_add 讲解,首先看函数的第三个参数events,他是一个整型,其没以为对应一种IO事件.上面fdevent_event_add()函数的额第三个参数是FDEVENT_IN,这是一个宏 /* * 用于标记文件描述符的状态 */ #define FDEVENT_IN BV(0) //文件描述符是否可写 #define FDEVENT_PRI BV(1) //不阻塞的可读高优先级的数据 poll #define FDEVEN

wifidog认证自带http服务器Lighttpd1.4.20源码分析之状态机返回response

前一篇介绍完了请求的处理,先面lighttpd将会把处理的结果返回给客户端.状态机进入CON_STATE_RESPONST_START.在这个状态中,服务器主要的工作在函数connection_handle_write_prepare.这个函数不算复杂,主要是根据客户端请求的method来设置response的headers,其实就是设置“Content-Length”的值.下面是函数代码,做了一些删减: static int connection_handle_write_prepare(se

wifidog 认证Lighttpd1.4.20源码分析之bitset.c(h) -------位集合的使用

使用一个比特位来表示一个事件的两种状态,即节省内存,又可以提高运行速度.在Lighttpd中,提供了一个bitset数据结构,用来管理使用一个比特位集合. 在bitset.h中,比特位集合的数据结构定义如下: typedef struct { size_t *bits; size_t nbits; } bitset; bits指向一个size_t类型的数组,存放bit集合.size_t类型通常被定义成一个无符号的整型(int或long),其长度和具体的机器有关,这个读者可以查阅相关的资料.nbi

Lighttpd1.4.20源码分析 笔记 状态机之response

在CON_STATE_RESPONSE_START状态中,服务器开始准备给客户端的response: case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "

Lighttpd1.4.20源码分析 笔记 状态机之错误处理和连接关闭

这里所说的错误有两种: 1.http协议规定的错误,如404错误: 2.服务器运行过程中的错误,如write错误. 对于http协议规定的错误,这里的"错误"是针对客户端的.lighttpd返回相应的错误提示文件之后,相当于顺利的完成了一次请求,只是结果和客户端想要的不一样而已. 对于服务器运行中的错误,状态机进入CON_STATE_ERROR状态.常见的错误原因:客户端提前断开连接.比如你不停的刷新页面,在你刷新的时候,前一次的连接没有完成,但被浏览器强行断开.对于服务器而言,刷新前

传奇源码分析-客户端(游戏逻辑处理源分析四)

现在假设玩家开始操作游戏:传奇的客户端源代码工程WindHorn一.CWHApp派生CWHWindow和CWHDXGraphicWindow.二.CWHDefProcess派生出CloginProcess.CcharacterProcess.CgameProcess客户端WinMain调用CWHDXGraphicWindow g_xMainWnd;创建一个窗口.客户端CWHDXGraphicWindow在自己的Create函数中调用了CWHWindow的Create来创建窗口,然后再调用自己的C

传奇源码分析-客户端(游戏逻辑处理源分析五 服务器端响应)

器执行流程:(玩家走动) GameSrv服务器ProcessUserHuman线程处理玩家消息:遍历UserInfoList列表,依次调用每个UserInfo的Operate来处理命令队列中的所有操作; pUserInfo->Operate()调用m_pxPlayerObject->Operate()调用.判断玩家if (!m_fIsDead),如果已死,则发送_MSG_FAIL消息.我们在前面看到过,该消息是被优先处理的.否则则调用WalkTo,并发送_MSG_GOOD消息给客户端.Walk

源码安装mysql5.6.20&&mysql主从设置(多实例做多个主从)

一.源码安装mysql5.6.20 1.编译环境安装 yum install wget gcc* make openssl openssl-devel openssl-clients -y && yum groupinstall "Development Libraries" -y 2.源码下载(软件见Linux部署源码包) wget -P /usr/local/src/ http://cdn.mysql.com/Downloads/MySQL-5.6/mysql-5.