一.概述
Lighttpd采用多进程网络服务模型。
进程分两种:监控进程watcher 和 工作进程 workers。
监控进程:fork工作进程并监视工作进程的数目,一旦有工作进程退出,监控进程立即fork新的工作进程。
工作进程:接收客户端请求并做出服务响应。
一般情况下,存在一个监控进程和多个工作进程。
max-worker值默认为0时,没有监控进程,只有一个工作进程。
关于初始化:Lighttpd很多地方内存申请都是采用calloc,malloc()和calloc()的主要区别是前者不能初始化所分配的内存空间,而后者能。
主流程入口文件:server.c
二.Lighttpd进程守护化
main函数中的函数daemonize调用使得Lighttpd进程转换为守护进程,从而脱离控制终端,在后台提供服务,避免了执行过程中的信息在终端上显示,也避免了服务被终端信息打断。
(启动时假如选项D可不守护化)
下面我们来分析下:
#ifdef HAVE_FORK
static void daemonize(void) {
#ifdef SIGTTOU
/* 下面用于屏蔽一些有关控制终端操作的信号,防止守护进程没有正常运作之前控制终端受到干扰退出或挂起 */
signal(SIGTTOU, SIG_IGN); //忽略后台进程写控制终端信号
#endif
#ifdef SIGTTIN
signal(SIGTTIN, SIG_IGN); //忽略后台进程读控制终端信号
#endif
#ifdef SIGTSTP
signal(SIGTSTP, SIG_IGN); //忽略终端挂起
#endif
/* 下面开始从普通进程转换为守护进程
* 目标1:后台运行。
* 做法:脱离控制终端->调用fork之后终止父进程,子进程被init收养,此步达到后台运行的目标。
*/
if (0 != fork()) exit(0);
/*,目标2:脱离控制终端,登陆会话和进程组。
* 做法:使用setsid创建新会话,成为新会话的首进程,则与原来的
* 登陆会话和进程组自动脱离,从而脱离控制终端。
* (上一步的fork保证了子进程不可能是一个会话的首进程,这是调用setsid的必要条件)
*/
if (-1 == setsid()) exit(0);
/* 上面已经完成了大部分工作,但是有的系统上,当会话首进程打开
* 一个尚未与任何会话相关联的终端设备时,该设备自动作为控制
* 终端分配给该会话。
* 为避免该情况,我们再次fork进程,于是新进程不再是会话首进程。
* 会话首进程退出时可能会给所有会话内的进程发送SIGHUP,而该
* 信号默认是结束进程,故需要忽略该信号来防止孙子进程意外结束。
*/
signal(SIGHUP, SIG_IGN);
if (0 != fork()) exit(0);
/* 最后目标:改变工作目录到根目录。
* 原因:进程活动时,其工作目录所在的文件系统不能卸下。
*/
if (0 != chdir("/")) exit(0);
}
#endif
进程属于一个进程组(一个或多个进程的集合),登陆会话是包含一个或多个进程组的集合,这些进程组共享一个控制终端。
三.多进程网络服务模型
Lighttpd一开始是单进程的,在完成一组公共操作后开始转换为多进程。
代码框架如下:
具体代码分析注释:
#ifdef USE_ALARM
signal(SIGALRM, signal_handler);
/* setup periodic timer (1 second) */
if (setitimer(ITIMER_REAL, &interval, NULL)) { //每隔一秒产生一个ALARM
log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed");
return -1;
}
getitimer(ITIMER_REAL, &interval);
#endif
#ifdef HAVE_FORK
/* start watcher and workers */
num_childs = srv->srvconf.max_worker; //存放最大的子进程的数目
if (num_childs > 0) {
int child = 0; //child变量用于标记是否为子进程,0代表父进程,1代表子进程
while (!child && !srv_shutdown && !graceful_shutdown) { //子进程不可进入,srv_shutdown=1 或 graceful_shutdown=1时父进程跳出
if (num_childs > 0) {
switch (fork()) { //创建子进程
case -1:
return -1;
case 0:
child = 1; //子进程标记
break;
default:
num_childs--; //父进程
break;
}
} else { //子进程产生完毕
int status; //保存子进程退出状态
if (-1 != wait(&status)) { //阻塞等待子进程退出,收尸
/**
* one of our workers went away
*/
num_childs++; //表示可以再产生新的子进程
} else<