Lighttpd的工作模式是可以配置的,一般是多进程的,一个监控进程,其他是工作进程。在配置文件lighttpd.conf中,如果有配置项server.max-worker=6(举例为6),即lighttpd启动后会创建6个工作进程,1个监控进程;如果server.max-worker=0,或者该项没有,则只创建一个工作进程,没有监控进程。
具体代码如下:
#ifdefHAVE_FORK
/* start watcher and workers */
num_childs = srv->srvconf.max_worker; //取得配置的工作进程数目,如果为0则直接跳过以下的代码,只有一个进程。
if (num_childs > 0) {
int child = 0;
while (!child &&!srv_shutdown && !graceful_shutdown) {
if (num_childs > 0) {
switch (fork()) {
case -1:
return -1;
case 0:
child = 1;//子进程,将跳出循环往下执行
break;
default:
num_childs--;//父进程,将待创建的进程数减小,父进程的child还为0
break;
}
} else {//此处父进程创建完所有的工作进程后等待
int status;
if (-1 !=wait(&status)) {
/**
* one of our workers went away
*/
num_childs++;//父进程保持睡眠,但是一旦发现有子进程退出,父进程就苏醒,将待创建的子进程数数目增1,下一个while循环将创建新的工作子进程
} else {
switch(errno) {
case EINTR:
/**
* if we receive a SIGHUP we have to close ourlogs ourself as we don't
* have the mainloop who can help us here
*/
if(handle_sig_hup) {
handle_sig_hup= 0;
log_error_cycle(srv);
/**
* forward to all procs in the process-group
*
* we also send it ourself
*/
if(!forwarded_sig_hup) {
forwarded_sig_hup= 1;
kill(0,SIGHUP);
}
}
break;
default:
break;
}
}
}
}
/**
* for the parent this is the exit-point
*/
if (!child) {父进程执行到这,表示服务器程序要退出了,于是作一些清理的工作,包括终止所有的工作进程。
/**
* kill all children too
*/
if (graceful_shutdown) {
kill(0, SIGINT);
} else if (srv_shutdown) {
kill(0, SIGTERM);
}
log_error_close(srv);
network_close(srv);
connections_free(srv);
plugins_free(srv);
server_free(srv);
return 0;
}
}
#endif
Lighttpd启动后进入监听模式,如果有新的连接请求,则创建一个数据结构管理该连接,该连接被赋予不同的11个连接状态,如下图所示,以下具体详细描述不同的状态:
序列 |
状态 |
状态CON_STATE_ |
含义 |
1 |
connect |
CONNECT |
等待连接 |
2 |
reqstart |
REQUEST_START |
等待读 |
3 |
read |
READ |
读HTTP请求头数据 |
4 |
reqend |
REQUEST_END |
解析客户端请求数据 |
5 |
readpost |
READ_POST |
读HTTP请求体数据 |
6 |
handlereq |
HANDLE_REQUEST |
内部处理请求,可能导致等待子请求 |
7 |
respstart |
RESPONSE_START |
准备响应头数据 |
8 |
write |
WRITE |
写响应数据,包括响应头和响应体 |
9 |
respend |
RESPONSE_END |
响应完成,进行清理及日志工作 |
10 |
error |
ERROR |
重置连接,包括关闭 |
11 |
close |
CLOSE |
关闭连接 |
Lighttpd主循环支持多种多路复用技术,本文只探讨linux的epoll模型。
如下所示:fdevents*fdevent_init(server *srv, size_t maxfds, fdevent_handler_t type)
{
………
caseFDEVENT_HANDLER_LINUX_SYSEPOLL:
if(0 != fdevent_linux_sysepoll_init(ev)) {
log_error_write(srv,__FILE__, __LINE__, "S",
"event-handlerlinux-sysepoll failed, try to set server.event-handler = \"poll\" or\"select\"");
gotoerror;
}
returnev;
………
}
int network_register_fdevents(server *srv){
size_ti;
if(-1 == fdevent_reset(srv->ev)) {
return-1;
}
/*register fdevents after reset */
for(i = 0; i < srv->srv_sockets.used; i++) {
server_socket*srv_socket = srv->srv_sockets.ptr[i];
fdevent_register(srv->ev,srv_socket->fd, network_server_handle_fdevent, srv_socket);
fdevent_event_set(srv->ev,&(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN);
}
return0;
}
if((n = fdevent_poll(srv->ev, 1000)) > 0) {//suyi kernel poll
/*n is the number of events */
intrevents;
intfd_ndx;
fd_ndx= -1;
do{
fdevent_handlerhandler;
void*context;
handler_tr;
fd_ndx = fdevent_event_next_fdndx (srv->ev,fd_ndx);//遍历整个触发的事件
if(-1 == fd_ndx) break; /* not all fdevent handlers know how many fds got anevent */
revents= fdevent_event_get_revent (srv->ev, fd_ndx);
fd = fdevent_event_get_fd (srv->ev, fd_ndx);
handler= fdevent_get_handler(srv->ev, fd); //此处的handler由3.2中的函数进行注册
context= fdevent_get_context(srv->ev, fd);
switch(r =(*handler)(srv, context, revents)) {//回调函数执行
caseHANDLER_FINISHED:
caseHANDLER_GO_ON:
caseHANDLER_WAIT_FOR_EVENT:
caseHANDLER_WAIT_FOR_FD:
break;
caseHANDLER_ERROR:
/*should never happen */
SEGFAULT();
break;
default:
log_error_write(srv,__FILE__, __LINE__, "d", r);
break;
}
}while (--n > 0); //epoll_wait返回的触发事件的个数
}else if (n < 0 && errno != EINTR) { //epoll_wait发生错误
log_error_write(srv,__FILE__, __LINE__, "ss",
"fdevent_pollfailed:",
strerror(errno));
}