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的初始化工作已经全部完成了。
额,没想到初始化讲了这么多。还是等下一篇再讲使用吧。。。

posted @ 2010-03-17 21:31  kernel@hcy  阅读(4491)  评论(0编辑  收藏  举报