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