Lighttpd1.4.20源码分析 笔记 网络服务主模型

本文详细分析了Lighttpd的源码,阐述了其进程分为监控进程和工作进程,监控进程负责fork和监控工作进程,工作进程处理客户端请求。Lighttpd通过daemonize函数实现进程守护化,确保服务在后台稳定运行。文章深入探讨了Lighttpd的多进程网络服务模型,包括子进程的初始化、连接管理、超时检测和事件处理等关键步骤,揭示了Lighttpd如何高效处理网络请求。
摘要由CSDN通过智能技术生成

一.概述

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<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值