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

前面讲了lighttpd的fdevent系统的初始化过程。这篇要看一看lighttpd是怎样使用fdevent系统的。讲解的过程中,会详细的分析fdevent的源代码。
首先还是从server.c的main函数入手。在程序的初始化过程中,当完成fdevent的初始化之后,第一个需要fdevent处理的事情就是将在初始化网络的过程中得到的监听fd(socket函数的返回值)注册的fdevent系统中。调用的是network_register_fdevents()函数,定义在network.c文件中:

/**
 * 在fd events系统中注册监听socket。
 * 这个函数在子进程中被调用。
 */
 int network_register_fdevents(server * srv)
{
    size_t i;
    if (-1 == fdevent_reset(srv->ev)){return -1;}
    /*
     * register fdevents after reset
     */
    for (i = 0; i < srv->srv_sockets.used; i++)
    {
        server_socket *srv_socket = srv->srv_sockets.ptr[i];
        fdevent_register(srv->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket);
        fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN);
    }
    return 0;
}

函数的重点是for循环,它遍历所有的监听fd并将其注册到fdevent系统中。在初始化网络的过程中,调用socket函数之后,将其返回值(监听fd)保存在server结构体的srv_sockets成员中,这个成员是一个server_socket_array结构体,而server_socket_array结构体是server_socket结构体的指针数组。server_socket结构体定义如下:

typedef struct
{
        sock_addr addr;           //socket fd对应的的地址。
         int fd;                     //socket()函数返回的监听fd
         int fde_ndx;                 //和fd相同。
         buffer *ssl_pemfile;
        buffer *ssl_ca_file;
        buffer *ssl_cipher_list;
        unsigned short ssl_use_sslv2;
        unsigned short use_ipv6;    //标记是否使用ipv6
         unsigned short is_ssl;
        buffer *srv_token;
#ifdef USE_OPENSSL
        SSL_CTX *ssl_ctx;
 #endif
        unsigned short is_proxy_ssl;
} server_socket;

这里我们主要看前三个成员,前两个成员很简单,对于第三个成员,作者的本意应该是保存fd对应的fdnode在fdevents结构体中fdarray数组中的下标,但是程序在存储fdnode时候是以fd最为下标存储的(后面的fdevent_register函数中),所以通常fde_ndx==fd。
下面看一看fdevent_register()函数,在fdevent.c中定义:

int fdevent_register(fdevents * ev, int fd, fdevent_handler handler, void *ctx)
{
    fdnode *fdn;
    fdn = fdnode_init();
    fdn->handler = handler;
    fdn->fd = fd;
    fdn->ctx = ctx;
    ev->fdarray[fd] = fdn; //使用文件描述符作为数组的下标。可以将查询
 //的时间变为 O(1)
     return 0;
}

在这个函数中,创建了一个fdnode结构体的实例,然后对其成员赋值。最后,以fd为下标将这个实例存如fdevents结构体中的fdarray数组中。关于第三个参数:fdevent_handler handler,这是一个函数指针,其定义为typedef handler_t(*fdevent_handler) (void *srv, void *ctx, int revents)。这个函数指针对应XXX_handle_fdevent()类型的函数。比如network.c/ network_server_handle_fdevent() ,connections.c/ connection_handle_fdevent()。这些函数的作用是在fdevent系统检测到fd有IO事件发生时,处理这些IO事件。比如,network_server_handle_fdevent()处理监听fd(socket函数的返回值)发生的IO事件,connection_handle_fdevent()处理连接fd(accept函数的返回值)发生的IO事件。除了上面的两个函数,还有stat_cacahe.c/stat_cache_handle_fdevent(),mod_cgi.c/cgi_handle_fdevent(),mod_fastcgi.c/ fcgi_handle_fdevent(),mod_proxy.c/ proxy_handle_fdevent()和mod_scgi.c/scgi_handle_fdevent()等。在后面的讲解中,主要围绕network_server_handle_fdevent()和connection_handle_fdevent(),其他的函数有兴趣的读者可以自行查看。
接着,在for循环中调用(fdevent.c)fdevent_event_add()函数:

int fdevent_event_add(fdevents * ev, int *fde_ndx, int fd, int events)
{
    int fde = fde_ndx ? *fde_ndx : -1;
    if (ev->event_add)
        fde = ev->event_add(ev, fde, fd, events)
    if (fde_ndx)
        *fde_ndx = fde;
    return 0;
}

函数中调用了fdevents结构体中event_add函数指针对应的函数。前面我们已经假设系统使用epoll,那么我们就去看看fdevent_linux_sysepoll.c中的fdevent_linux_sysepoll_event_add()函数,这个函数的地址在初始化的时候被赋给fdevents中的event_add指针:

static int fdevent_linux_sysepoll_event_add(fdevents * ev, int fde_ndx, int fd, int events)
{
    struct epoll_event ep;
    int add = 0;
    if (fde_ndx == -1) //描述符不在epoll的检测中,增加之。
         add = 1;
    memset(&ep, 0, sizeof(ep));
    ep.events = 0;
    /**
     * 在ep中设置需要监听的IO事件。
     * EPOLLIN : 描述符可读。
     * EPOLLOUT :描述符可写。
     * 其他的事件还有:EPOLLRDHUP , EPOLLPRI, EPOLLERR, EPOLLHUP, EPOLLET, EPOLLONESHOT等。
     */
    if (events & FDEVENT_IN)
        ep.events |= EPOLLIN;
    if (events & FDEVENT_OUT)
        ep.events |= EPOLLOUT;
    /*
     * EPOLLERR :描述符发生错误。
     * EPOLLHUP :描述符被挂断。通常是连接断开。
     */
    ep.events |= EPOLLERR | EPOLLHUP /* | EPOLLET */ ;
    ep.data.ptr = NULL;
    ep.data.fd = fd;
    /*
     * EPOLL_CTL_ADD : 增加描述符fd到ev->epoll_fd中,并关联ep中的事件到fd上。
     * EPOLL_CTL_MOD : 修改fd所关联的事件。
     */
    if (0 != epoll_ctl(ev->epoll_fd, add ?EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ep))
    {
        fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n",__FILE__,__LINE__, strerror(errno));
        SEGFAULT();
        return 0;
    }
    return fd;
}

本文章由 http://www.wifidog.pro/2015/04/21/wifidog%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90lighttpd%E4%BD%BF%E7%94%A8-1.html 整理编辑,转载请注明出处

标签: wifidog原理, wifidog认证, wifidog源码, wifidog分析