boa阅读笔记5
boa阅读笔记5
开源中国精神科主任 发表于3年前
boa阅读笔记5
  • 发表于 3年前
  • 阅读 12
  • 收藏 0
  • 点赞 0
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

    create_common_env();
    build_needs_escape();

    if (max_connections < 1) {
        struct rlimit rl;

        /* has not been set explicitly */
        c = getrlimit(RLIMIT_NOFILE, &rl);    //获得系统的限制
        if (c < 0) {
            perror("getrlimit");
            exit(1);
        }
        max_connections = rl.rlim_cur;        //根据系统限制设置最大连接数
    }

    /* background ourself */
    if (do_fork) {                            //父进程退出,子进程转换为守护进程
        switch(fork()) {
        case -1:
            /* error */
            perror("fork");
            exit(1);
            break;
        case 0:
            /* child, success */
            break;
        default:
            /* parent, success */
            exit(0);
			//break?????
            break;
        }
    }

我们接着从create_common_env开始介绍。

void create_common_env()
{
    int index = 0, i;


    /* NOTE NOTE NOTE:
       If you (the reader) someday modify this chunk of code to
       handle more "common" CGI environment variables, then bump the
       value COMMON_CGI_COUNT in defines.h UP

       Also, in the case of document_root and server_admin, two variables
       that may or may not be defined depending on the way the server
       is configured, we check for null values and use an empty
       string to denote a NULL value to the environment, as per the
       specification. The quote for which follows:

       "In all cases, a missing environment variable is
       equivalent to a zero-length (NULL) value, and vice versa."
     */
    common_cgi_env[index++] = env_gen_extra("PATH",
                                            ((cgi_path != NULL) ? cgi_path : DEFAULT_PATH), 0);
    common_cgi_env[index++] = env_gen_extra("SERVER_SOFTWARE", SERVER_VERSION, 0);
    common_cgi_env[index++] = env_gen_extra("SERVER_NAME", server_name, 0);
    common_cgi_env[index++] = env_gen_extra("GATEWAY_INTERFACE", CGI_VERSION, 0);

    common_cgi_env[index++] =
        env_gen_extra("SERVER_PORT", simple_itoa(server_port), 0);

    /* NCSA and APACHE added -- not in CGI spec */
    /* common_cgi_env[index++] = env_gen_extra("DOCUMENT_ROOT", document_root); */

    /* NCSA added */
    /* common_cgi_env[index++] = env_gen_extra("SERVER_ROOT", server_root); */

    /* APACHE added */
    common_cgi_env[index++] = env_gen_extra("SERVER_ADMIN", server_admin, 0);
    common_cgi_env[index] = NULL;

    /* Sanity checking -- make *sure* the memory got allocated */
    if (index > COMMON_CGI_COUNT) {
        log_error_time();
        fprintf(stderr, "COMMON_CGI_COUNT not high enough.\n");
        exit(1);
    }

    for(i = 0;i < index;++i) {
        if (common_cgi_env[i] == NULL) {
            log_error_time();
            fprintf(stderr, "Unable to allocate a component of common_cgi_env - out of memory.\n");
            exit(1);
        }
    }
}

上面的代码应该是在设置cgi的环境变量,并最后进行一次环境变量检测。

void build_needs_escape(void)
{
    unsigned int a, b;
    const unsigned char special[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789"
        "-_.!~*'():@&=+$,/?";
    /* 21 Mar 2002 - jnelson - confirm with Apache 1.3.23 that '?'
     * is safe to leave unescaped.
     */
    unsigned short i, j;

    b = 1;
    for (a=0; b!=0; a++) b=b<<1;
    /* I found $a bit positions available in an unsigned long. */
    if (a < NEEDS_ESCAPE_WORD_LENGTH) {
        fprintf(stderr,
                "NEEDS_ESCAPE_SHIFT configuration error -- "\
                "%d should be <= log2(%d)\n",
                NEEDS_ESCAPE_SHIFT, a);
        exit(1);
    } else if (a >= 2*NEEDS_ESCAPE_WORD_LENGTH) {
        /* needs_escape_shift configuration suboptimal */
    } else {
        /* Ahh, just right! */;
    }
    memset(_needs_escape, ~0, sizeof(_needs_escape));
    for(i = 0; i < sizeof(special) - 1; ++i) {
        j=special[i];
        if (j>=NEEDS_ESCAPE_BITS) {
            /* warning: character $j will be needlessly escaped. */
        } else {
            _needs_escape[NEEDS_ESCAPE_INDEX(j)]&=~NEEDS_ESCAPE_MASK(j);
        }
    }
}

看函数名应该跟编解码有关,浏览代码后初步确定是创建了程序需要的编码对照表,方便server快速编解码。

    /* main loop */
    timestamp();

    status.requests = 0;
    status.errors = 0;

    start_time = current_time;

    #if defined _kqueue
        kqueue_loop(server_s);

    #elif defined _epoll
        epoll_loop(server_s);

    #else
        select_loop(server_s);

    #endif

    return 0;
}

最后就是进入主循环了,先是记录当前时间,然后根据系统情况以先后次序选择kqueue、epoll、和select之一进行socket端口的监听循环。

void select_loop(int server_s)
{
    FD_ZERO(&block_read_fdset);
    FD_ZERO(&block_write_fdset);
    /* set server_s and req_timeout */
    req_timeout.tv_sec = (ka_timeout ? ka_timeout : REQUEST_TIMEOUT);
    req_timeout.tv_usec = 0l;   /* reset timeout */

    /* preset max_fd */
    max_fd = -1;

    while (1) {
        if (sighup_flag)
            sighup_run();
        if (sigchld_flag)
            sigchld_run();
        if (sigalrm_flag)
            sigalrm_run();

        if (sigterm_flag) {
            if (sigterm_flag == 1)
                sigterm_stage1_run(server_s);
            if (sigterm_flag == 2 && !request_ready && !request_block) {
                sigterm_stage2_run();
            }
        }

        /* reset max_fd */
        max_fd = -1;

        if (request_block)
            /* move selected req's from request_block to request_ready */
            fdset_update();

        /* any blocked req's move from request_ready to request_block */
        process_requests(server_s);

        if (!sigterm_flag && total_connections < (max_connections - 10)) {
            BOA_FD_SET(server_s, &block_read_fdset); /* server always set */
        }

        req_timeout.tv_sec = (request_ready ? 0 :
                              (ka_timeout ? ka_timeout : REQUEST_TIMEOUT));
        req_timeout.tv_usec = 0l;   /* reset timeout */

        if (select(max_fd + 1, &block_read_fdset,
                   &block_write_fdset, NULL,
                   (request_ready || request_block ? &req_timeout : NULL)) == -1) {
            /* what is the appropriate thing to do here on EBADF */
            if (errno == EINTR)
                continue;   /* while(1) */
            else if (errno != EBADF) {
                DIE("select");
            }
        }

        time(&current_time);
        if (FD_ISSET(server_s, &block_read_fdset))
            pending_requests = 1;
    }
}

因为对select比较熟悉,因此拿select_loop来分析。在主循环中先对状态标志进行了分析和处理,如果有请求阻塞,则把请求移到就绪队列进行处理,然后对请求进行超时处理,最后再次进行select监听读写请求。

对boa的解读就先到这里了,由于对http服务的了解还不多,因此也无法进行更细致的业务分析,只能讲解个大概,以后研究http协议时再来做详细的分析。

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 1
博文 18
码字总数 26787
×
开源中国精神科主任
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: