1. 程式人生 > >Lighttpd1.4.20原始碼分析 筆記 fdevent系統-初始化

Lighttpd1.4.20原始碼分析 筆記 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\"\n",
        __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\n", __FILE__, __LINE__);
            return NULL;
        }
        break;
    case FDEVENT_HANDLER_SELECT:
        if (0 != fdevent_select_init(ev))
        {
            fprintf(stderr, "%s.%d: event-handler select failed\n", __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\"\n",
                    __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\"\n",
                    __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\"\n",
                    __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\"\n",
                    __FILE__, __LINE__);
            return NULL;
        }
        break;
    default:
        fprintf(stderr, "%s.%d: event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\"\n",
                __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\"\n",
                __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\"\n",
                __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的初始化工作全部完成。