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(¤t_time); if (FD_ISSET(server_s, &block_read_fdset)) pending_requests = 1; } }
因为对select比较熟悉,因此拿select_loop来分析。在主循环中先对状态标志进行了分析和处理,如果有请求阻塞,则把请求移到就绪队列进行处理,然后对请求进行超时处理,最后再次进行select监听读写请求。
对boa的解读就先到这里了,由于对http服务的了解还不多,因此也无法进行更细致的业务分析,只能讲解个大概,以后研究http协议时再来做详细的分析。