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协议时再来做详细的分析。