fdevent系统-初始化

C程序在进行真正的编译之前都要进行预编译。

我们看看fdevent系统中的一些宏:

#if defined(HAVE_EPOLL_CTL) && defined(HAVE_SYS_EPOLL_H)
# if defined HAVE_STDINT_H
#  include <stdint.h>
# endif
# define USE_LINUX_EPOLL
# include <sys/epoll.h>
#endif

#if defined HAVE_POLL && (defined(HAVE_SYS_POLL_H) || defined(HAVE_POLL_H))
# define USE_POLL
# ifdef HAVE_POLL_H
#  include <poll.h>
# else
#  include <sys/poll.h>
# endif
# if defined HAVE_SIGTIMEDWAIT && defined(__linux__)
#  define USE_LINUX_SIGIO
#  include <signal.h>
# endif
#endif
//……

上面的宏判断系统中是否有对应的多路IO系统,如果有,就定义对应的USE_XXX宏。

预编译完这些宏以后,对于当前系统中有的多路IO系统,就会有对应的USE_XXX符号被定义。预编译器接着运行,将那些不需要的代码都忽略。

fdevent.h中对所有可能的多路IO系统都定义了初始化函数:

 int fdevent_select_init(fdevents * ev);
 int fdevent_poll_init(fdevents * ev);
 int fdevent_linux_rtsig_init(fdevents * ev);
 int fdevent_linux_sysepoll_init(fdevents * ev);
 int fdevent_solaris_devpoll_init(fdevents * ev);
 int fdevent_freebsd_kqueue_init(fdevents * ev);

因此,对于系统中没有的多路IO系统对应的初始化函数,预编译结束后,这些初始化函数被定义为报错函数。如epoll对应的为:

#ifdef USE_LINUX_EPOLL
/* 当定义了epoll时,epoll的函数实现代码 */
#else 
/* 当未定义epoll时,epoll只实现init函数(这是必须的,因为该函数在fdevent.h中定义了),并将它实现为报错函数 */
int fdevent_linux_sysepoll_init(fdevents *ev) {
    UNUSED(ev);

    fprintf(stderr, "%s.%d: linux-sysepoll not supported, try to set server.event-handler = "poll" or "select"
",
        __FILE__, __LINE__);

    return -1;
}
#endif

预编译后,开始真正的编译。我们假设系统中只有epoll。

首先,我们看一看配置中有关fdevent的设置。进入configfile.c文件中的config_set_defaults()函数。函数的一开始就有这么一个定义:

    struct ev_map
    {
        fdevent_handler_t et;
        const char *name;
    } event_handlers[] =
    {
        /*
         * - poll is most reliable - select works everywhere -
* linux-* are experimental
         */
#ifdef USE_POLL
        {FDEVENT_HANDLER_POLL, "poll"},
#endif
#ifdef USE_SELECT
        {FDEVENT_HANDLER_SELECT, "select"},
#endif
#ifdef USE_LINUX_EPOLL
        {FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll"},
#endif
#ifdef USE_LINUX_SIGIO
        {FDEVENT_HANDLER_LINUX_RTSIG, "linux-rtsig"},
#endif
#ifdef USE_SOLARIS_DEVPOLL
        {FDEVENT_HANDLER_SOLARIS_DEVPOLL, "solaris-devpoll"},
#endif
#ifdef USE_FREEBSD_KQUEUE
        {FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue"},
        {FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue"},
#endif
        {FDEVENT_HANDLER_UNSET, NULL}
    };

上面定义了一个struct ev_map类型的数组。数组的内容是当前系统中存在的多路IO系统的类型和名称。这里排序很有意思,从注释中可以看出,poll排在最前因为最可靠,select其次因为支持最广泛,epoll第三因为是最好的。

在这里,如果配置文件有配置,那么按配置文件来,如果没配置,则取上面数组中的第一个。以下是配置文件的格式:

## set the event-handler (read the performance
##section in the manual)
server.event-handler = "freebsd-kqueue" # needed on OS X

接下来我们看server.c中的main函数。

前面有一些是设置fd数量的,其中select比较特殊需要特别处理,fd数量一个是系统的限制,一个是用户配置的限制。

当程序产生子进程后,在子进程中执行的第一条语句就是初始化fdevent系统:

    if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) {
        log_error_write(srv, __FILE__, __LINE__,
                "s", "fdevent_init failed");
        return -1;
    }

进入fdevent_init()函数:

/**
 * 初始化文件描述符事件数组fdevent
 */
fdevents *fdevent_init(size_t maxfds, fdevent_handler_t type)
{
    fdevents *ev;

    //内存被初始化为0
    ev = calloc(1, sizeof(*ev));

    //分配数组
    ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray));
    ev->maxfds = maxfds;

    //根据设定的多路IO的类型进行初始化。
    switch (type)
    {
    case FDEVENT_HANDLER_POLL:
        if (0 != fdevent_poll_init(ev))
        {
            fprintf(stderr, "%s.%d: event-handler poll failed
", __FILE__, __LINE__);
            return NULL;
        }
        break;
    case FDEVENT_HANDLER_SELECT:
        if (0 != fdevent_select_init(ev))
        {
            fprintf(stderr, "%s.%d: event-handler select failed
", __FILE__, __LINE__);
            return NULL;
        }
        break;
    case FDEVENT_HANDLER_LINUX_RTSIG:
        if (0 != fdevent_linux_rtsig_init(ev))
        {
            fprintf(stderr, "%s.%d: event-handler linux-rtsig failed, try to set server.event-handler = "poll" or "select"
",
                    __FILE__, __LINE__);
            return NULL;
        }
        break;
    case FDEVENT_HANDLER_LINUX_SYSEPOLL:
        if (0 != fdevent_linux_sysepoll_init(ev))
        {
            fprintf(stderr, "%s.%d: event-handler linux-sysepoll failed, try to set server.event-handler = "poll" or "select"
",
                    __FILE__, __LINE__);
            return NULL;
        }
        break;
    case FDEVENT_HANDLER_SOLARIS_DEVPOLL:
        if (0 != fdevent_solaris_devpoll_init(ev))
        {
            fprintf(stderr, "%s.%d: event-handler solaris-devpoll failed, try to set server.event-handler = "poll" or "select"
",
                    __FILE__, __LINE__);
            return NULL;
        }
        break;
    case FDEVENT_HANDLER_FREEBSD_KQUEUE:
        if (0 != fdevent_freebsd_kqueue_init(ev))
        {
            fprintf(stderr, "%s.%d: event-handler freebsd-kqueue failed, try to set server.event-handler = "poll" or "select"
",
                    __FILE__, __LINE__);
            return NULL;
        }
        break;
    default:
        fprintf(stderr, "%s.%d: event-handler is unknown, try to set server.event-handler = "poll" or "select"
",
                __FILE__, __LINE__);
        return NULL;
    }

    return ev;
}

fdevent_init()函数根据fdevent_handler_t的值调用相应的初始化函数。我们进入fdevent_linux_sysepoll_init()函数:

int fdevent_linux_sysepoll_init(fdevents * ev)
{
    ev->type = FDEVENT_HANDLER_LINUX_SYSEPOLL;
#define SET(x) 
    ev->x = fdevent_linux_sysepoll_##x; 
    /* 通过SET宏对fdevents结构体中的函数指针赋值。然后创建epoll,最后做一些设置。*/
    SET(free);
    SET(poll);
    SET(event_del);
    SET(event_add);
    SET(event_next_fdndx);
    SET(event_get_fd);
    SET(event_get_revent);
    //创建epoll
    if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds)))
    {
        fprintf(stderr, "%s.%d: epoll_create failed (%s), try 
to set server.event-handler = "poll" or "select"
",
                __FILE__, __LINE__, strerror(errno));
        return -1;
    }
    //设置epoll_fd为运行exec()函数时关闭。
    if (-1 == fcntl(ev->epoll_fd, F_SETFD, FD_CLOEXEC))
    {
        fprintf(stderr,    "%s.%d: epoll_create failed (%s), try 
to set server.event-handler = "poll" or "select"
",
                __FILE__, __LINE__, strerror(errno));
        close(ev->epoll_fd);
        return -1;
    }
    //创建fd事件数组。在epoll_wait函数中使用。
    //存储发生了IO事件的fd和对应的IO事件。
    ev->epoll_events = malloc(ev->maxfds * 
sizeof(*ev->epoll_events));
    return 0;
}

至此fdevent的初始化工作全部完成。

文章导航