zoukankan      html  css  js  c++  java
  • Lighttpd1.4.20源码分析之fdevent系统(2)初始化

    前面讲了lighttpd的fdevents结构体以及fdevent系统的对外接口,这一篇将解析一下fdevent系统初始化。
    C程序在进行真正的编译之前都要进行预编译,那么,我们就先来看看fdevent系统中的一些宏。首先是fdevent.h开头的一些宏:

    1 #if defined(HAVE_EPOLL_CTL) && defined(HAVE_SYS_EPOLL_H)
    2 # if defined HAVE_STDINT_H
    3 # include <stdint.h>
    4 # endif
    5 # define USE_LINUX_EPOLL
    6 # include <sys/epoll.h>
    7  #endif
    8
    9
    10
    11  #if defined HAVE_POLL && (defined(HAVE_SYS_POLL_H) || defined(HAVE_POLL_H))
    12 # define USE_POLL
    13 # ifdef HAVE_POLL_H
    14 # include <poll.h>
    15 # else
    16 # include <sys/poll.h>
    17 # endif
    18 # if defined HAVE_SIGTIMEDWAIT && defined(__linux__)
    19 # define USE_LINUX_SIGIO
    20 # include <signal.h>
    21 # endif
    22  #endif
    23
    24  #if defined HAVE_SELECT
    25 # ifdef __WIN32
    26 # include <winsock2.h>
    27 # endif
    28 # define USE_SELECT
    29 # ifdef HAVE_SYS_SELECT_H
    30 # include <sys/select.h>
    31 # endif
    32  #endif
    33
    34  #if defined HAVE_SYS_DEVPOLL_H && defined(__sun)
    35 # define USE_SOLARIS_DEVPOLL
    36 # include <sys/devpoll.h>
    37  #endif
    38
    39  #if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
    40 # define USE_FREEBSD_KQUEUE
    41 # include <sys/event.h>
    42  #endif
    43
    44  #if defined HAVE_SYS_PORT_H && defined HAVE_PORT_CREATE
    45 # define USE_SOLARIS_PORT
    46 # include <sys/port.h>
    47  #endif

    通过上面的宏判断系统中是否有对应的多路IO系统,如果有,就定义对应的USE_XXX宏,用来方便后面程序的盘算。
    预编译完这些宏以后,对于当前系统中有的多路IO系统,就会有对应的USE_XXX符号被定义。预编译器接着运行,将那些不需要的代码都忽略。由于fdevent.h中对所有可能的多路IO系统都定义了初始化函数:

    1 int fdevent_select_init(fdevents * ev);
    2  int fdevent_poll_init(fdevents * ev);
    3  int fdevent_linux_rtsig_init(fdevents * ev);
    4  int fdevent_linux_sysepoll_init(fdevents * ev);
    5  int fdevent_solaris_devpoll_init(fdevents * ev);
    6  int fdevent_freebsd_kqueue_init(fdevents * ev);

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

    1 int fdevent_linux_sysepoll_init(fdevents * ev)
    2 {
    3 UNUSED(ev);
    4 fprintf(stderr, "%s.%d: linux-sysepoll not supported,
    5  try to set server.event-handler = \"poll\" or \"select\"\n",
    6   __FILE__, __LINE__);
    7 return -1;
    8 }

    等预编译器执行完后,进行真正的编译。这里,我们假设系统中只有epoll。
    还是从server.c中的main函数开始。
    首先,我们要看一看在配置中,有关fdevent的设置。进入configfile.c文件中的config_set_defaults()函数。函数的一开始就有这么一个定义:

    1 struct ev_map
    2 {
    3 fdevent_handler_t et;
    4 const char *name;
    5 } event_handlers[] =
    6 {
    7 /*
    8 * - poll is most reliable - select works everywhere -
    9 * linux-* are experimental
    10 */
    11 #ifdef USE_POLL
    12 {FDEVENT_HANDLER_POLL, "poll"},
    13 #endif
    14 #ifdef USE_SELECT
    15 {FDEVENT_HANDLER_SELECT, "select"},
    16 #endif
    17 #ifdef USE_LINUX_EPOLL
    18 {FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll"},
    19 #endif
    20 #ifdef USE_LINUX_SIGIO
    21 {FDEVENT_HANDLER_LINUX_RTSIG, "linux-rtsig"},
    22 #endif
    23 #ifdef USE_SOLARIS_DEVPOLL
    24 {FDEVENT_HANDLER_SOLARIS_DEVPOLL, "solaris-devpoll"},
    25 #endif
    26 #ifdef USE_FREEBSD_KQUEUE
    27 {FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue"},
    28 {FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue"},
    29 #endif
    30 {FDEVENT_HANDLER_UNSET, NULL}
    31 };

    这个结构体定义了多路IO系统的类型和名称。那些宏的作用就不多说了。上面的语句定义了一个struct ev_map类型的数组。数组的内容是当前系统中存在的多路IO系统的类型和名称。这里排序很有意思,从注释中可以看出,poll排在最前因为最可靠,select其次因为支持最广泛,epoll第三因为是最好的。继续朝下走,

    1 if (srv->srvconf.event_handler->used == 0)
    2 {
    3 srv->event_handler = event_handlers[0].et;
    4 }
    5 else
    6 {
    7 for (i = 0; event_handlers[i].name; i++)
    8 {
    9 if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler->ptr))
    10 {
    11 srv->event_handler = event_handlers[i].et;
    12 break;
    13 }
    14 }
    15 }

    srv->srvconf.event_handler->used为0说明配置文件中没有配置所使用的多路IO系统。

    跑个题,在配置文件中是如下进行设置的:

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

    所设置的值就是上面定义的那个数组中元素的第二个成员(字符串)的值。
    如果配置文件中没有配置,那么就使用上面定义的数组的第一个元素的值。这也就是为什么定义的时候那个排序很有意思的原因。
    如果配置文件中有配置,那就按配置文件的来喽。(上面的代码删除了处理出错的部分)
    从congif_set_defaults()函数回来,继续在main函数中走。
    下面的语句设置最大描述符数量:

    1 if (srv->event_handler == FDEVENT_HANDLER_SELECT)
    2 {
    3 /*
    4 * select的硬限制。减去200是为了增加安全性,防止出现段错误。
    5 */
    6 srv->max_fds = FD_SETSIZE - 200;
    7 }
    8 else
    9 {
    10 srv->max_fds = 4096;
    11 }

    由于select()函数一次能处理的fd数量有限制,所以要特殊处理。FD_SETSIZT在select.h中定义。其他情况最大描述符数量都设置为4096.
    后面还有一个设置最大fd数量的地方:

    1 //根据实际设置情况,重新设置max_fds。
    2 if (srv->event_handler == FDEVENT_HANDLER_SELECT)
    3 {
    4 srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ?
    5 rlim.rlim_cur : FD_SETSIZE - 200;
    6 }
    7 else
    8 {
    9 srv->max_fds = rlim.rlim_cur;
    10 }

    这个是根据当前用户的限制来的。
    当程序产生子进程后,在子进程中执行的第一条语句就是初始化fdevent系统:

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

    进入fdevent_init()函数:

    1 /**
    2 * 初始化文件描述符事件数组fdevent
    3 */
    4 fdevents *fdevent_init(size_t maxfds, fdevent_handler_t type)
    5 {
    6 fdevents *ev;
    7
    8 //内存被初始化为0
    9 ev = calloc(1, sizeof(*ev));
    10
    11 //分配数组
    12 ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray));
    13 ev->maxfds = maxfds;
    14
    15 //根据设定的多路IO的类型进行初始化。
    16 switch (type)
    17 {
    18 case FDEVENT_HANDLER_POLL:
    19 if (0 != fdevent_poll_init(ev))
    20 {
    21 fprintf(stderr, "%s.%d: event-handler poll failed\n", __FILE__, __LINE__);
    22 return NULL;
    23 }
    24 break;
    25 case FDEVENT_HANDLER_SELECT:
    26 if (0 != fdevent_select_init(ev))
    27 {
    28 fprintf(stderr, "%s.%d: event-handler select failed\n", __FILE__, __LINE__);
    29 return NULL;
    30 }
    31 break;
    32 case FDEVENT_HANDLER_LINUX_RTSIG:
    33 if (0 != fdevent_linux_rtsig_init(ev))
    34 {
    35 fprintf(stderr, "%s.%d: event-handler linux-rtsig failed, try to set server.event-handler = \"poll\" or \"select\"\n",
    36 __FILE__, __LINE__);
    37 return NULL;
    38 }
    39 break;
    40 case FDEVENT_HANDLER_LINUX_SYSEPOLL:
    41 if (0 != fdevent_linux_sysepoll_init(ev))
    42 {
    43 fprintf(stderr, "%s.%d: event-handler linux-sysepoll failed, try to set server.event-handler = \"poll\" or \"select\"\n",
    44 __FILE__, __LINE__);
    45 return NULL;
    46 }
    47 break;
    48 case FDEVENT_HANDLER_SOLARIS_DEVPOLL:
    49 if (0 != fdevent_solaris_devpoll_init(ev))
    50 {
    51 fprintf(stderr, "%s.%d: event-handler solaris-devpoll failed, try to set server.event-handler = \"poll\" or \"select\"\n",
    52 __FILE__, __LINE__);
    53 return NULL;
    54 }
    55 break;
    56 case FDEVENT_HANDLER_FREEBSD_KQUEUE:
    57 if (0 != fdevent_freebsd_kqueue_init(ev))
    58 {
    59 fprintf(stderr, "%s.%d: event-handler freebsd-kqueue failed, try to set server.event-handler = \"poll\" or \"select\"\n",
    60 __FILE__, __LINE__);
    61 return NULL;
    62 }
    63 break;
    64 default:
    65 fprintf(stderr, "%s.%d: event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\"\n",
    66 __FILE__, __LINE__);
    67 return NULL;
    68 }
    69
    70 return ev;
    71 }

    fdevent_init()函数根据fdevent_handler_t的值调用相应的初始化函数。我们前面已经假设系统使用epoll,那么进入fdevent_linux_sysepoll_init()函数:

    1 int fdevent_linux_sysepoll_init(fdevents * ev)
    2 {
    3 ev->type = FDEVENT_HANDLER_LINUX_SYSEPOLL;
    4 #define SET(x) \
    5 ev->x = fdevent_linux_sysepoll_##x;
    6 SET(free);
    7 SET(poll);
    8 SET(event_del);
    9 SET(event_add);
    10 SET(event_next_fdndx);
    11 SET(event_get_fd);
    12 SET(event_get_revent);
    13 //创建epoll
    14 if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds)))
    15 {
    16 fprintf(stderr, "%s.%d: epoll_create failed (%s), try
    17 to set server.event-handler = \"poll\" or \"select\"\n",
    18 __FILE__, __LINE__, strerror(errno));
    19 return -1;
    20 }
    21 //设置epoll_fd为运行exec()函数时关闭。
    22 if (-1 == fcntl(ev->epoll_fd, F_SETFD, FD_CLOEXEC))
    23 {
    24 fprintf(stderr, "%s.%d: epoll_create failed (%s), try
    25 to set server.event-handler = \"poll\" or \"select\"\n",
    26 __FILE__, __LINE__, strerror(errno));
    27 close(ev->epoll_fd);
    28 return -1;
    29 }
    30 //创建fd事件数组。在epoll_wait函数中使用。存储发生了IO事件的fd和
    31 //对应的IO事件。
    32 ev->epoll_events = malloc(ev->maxfds *
    33 sizeof(*ev->epoll_events));
    34 return 0;
    35 }
    36

    函数中首先通过SET宏对fdevents结构体中的函数指针赋值。然后创建epoll,最后做一些设置。
    至此fdevent的初始化工作已经全部完成了。
    额,没想到初始化讲了这么多。还是等下一篇再讲使用吧。。。

  • 相关阅读:
    正则表达式中/i,/g,/ig,/gi,/m的区别和含义
    Git 使用中显示“Another git process seems to be running in this repository...”问题解决
    MySQL数据库中varchar与char类型的区别
    上传本地代码到gitHub过程详解
    PHP实现四种基本排序算法
    最优秀的5个Linux文本编辑器 狼人:
    11款有用的Web开发在线工具 狼人:
    Java.net网站新改版上线 狼人:
    WebMatrix进阶教程(3):如何实现某种样式 狼人:
    新界面新功能Firefox 4.0 RC重磅登场 狼人:
  • 原文地址:https://www.cnblogs.com/kernel_hcy/p/1688482.html
Copyright © 2011-2022 走看看