您的位置:首页 > 编程语言

boa源码分析(3)--代码结构

2012-08-27 17:38 531 查看
1  boa.c

主程序:

----1)  关闭文件
for(i=3;i<=1024;i++)
close(i);

----2)  设置进程权限掩码
umask(~0600);    rw- --- ---;

----3)  打开黑洞,并将标准输入输出指向它,
open("/dev/null", 0);
dup2(devnullfd, STDIN_FILENO);
dup2(devnullfd, STDOUT_FILENO);

----4)  更新时间戳,日志要用到。
time(¤t_time);

----5)解析命令行
-f
server_root = strdup(optaarg);
-r
chdir(optarg);
chroot(optarg);
chdir("/");
-d
do_fork=0;

----6)//确保服务器根目录是有效的。
fixup_server_root();

----7)//读取配置文件通过yyparse 确保必要的变量设置正确。
read_config_files();

----8)//打开access log,error log, [cgi log] ;并设置 close-on-exec为真,即在exec调用后,关闭文件描述符。
open_logs();

----9)//创建TCP socket,设置为nonblock ,同样设置 close-on-exec为真,这样,EXEC调用时,cgi不能向它写入。。。
server_s = create_server_socket();    //打开了地址复用功能 详见 unix网络编程。

---10)//指定各信号的handle
init_signals();

---11)//设置用户ID和进程组ID。
drop_privs();//降 特权

---12) Set up the environment variables that are common to all CGI scripts
 create_common_env();

---13)  fork子进程,父进程退出。之后子进程成为守护进程
if(do_fork)   switch(fork())

---14) 得到PID,用于产生独一无二的临时文件名或路径。
int pid = getpid();

---15)  更新时间戳,然后进入主循环。
timestamp();
select_loop(server_s)
{
1)清空,block_read_fdset、block_write_fdset;
2)设置server_s和请求超时时间。
3)进入while(1)
{
1)   处理sighup 、  sigchld 、 sigalrm、 sigterm等信号。
2)重设max_fd = -1;
3)   将合适的request从block链表里移到ready链表里。
if(reques_block)     fdset_update(); //

4)  process_requests(server_s);

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

6)   reset  timeout

7)   select调用,select(max_fd + 1, &block_read_fdset, &block_write_fdset, NULL, (request_ready || request_block ? &req_timeout : NULL))

8)更新当前时间,time(&curent_time);
9)  if (FD_ISSET(server_s, &block_read_fdset))   pending_requests = 1;

}

}


 一、先来看看 fdset_update()

boa里边有三个请求链表

request *request_ready = NULL;  /* ready list head */

request *request_block = NULL;   /* blocked list head */

request *request_free = NULL;      /* free list head */

struct request {                /* pending requests */
int fd;                     /* client's socket fd */
int status;                 /* see #defines.h */
time_t time_last;           /* time of last succ. op. */
char *pathname;             /* pathname of requested file */
int simple;                 /* simple request? */
int keepalive;              /* keepalive status */
int kacount;                /* keepalive count */

int data_fd;                /* fd of data */
unsigned long filesize;     /* filesize */
unsigned long filepos;      /* position in file */
char *data_mem;             /* mmapped/malloced char array */
int method;                 /* M_GET, M_POST, etc. */

char *logline;              /* line to log file */

char *header_line;          /* beginning of un or incompletely processed header line */
char *header_end;           /* last known end of header, or end of processed data */
int parse_pos;              /* how much have we parsed */
int client_stream_pos;      /* how much have we read... */

int buffer_start;           /* where the buffer starts */
int buffer_end;             /* where the buffer ends */

char *http_version;         /* HTTP/?.? of req */
int response_status;        /* R_NOT_FOUND etc. */

char *if_modified_since;    /* If-Modified-Since */
time_t last_modified;       /* Last-modified: */

char local_ip_addr[NI_MAXHOST]; /* for virtualhost */

/* CGI vars */

int remote_port;            /* could be used for ident */

char remote_ip_addr[NI_MAXHOST]; /* after inet_ntoa */

int is_cgi;                 /* true if CGI/NPH */
int cgi_status;
int cgi_env_index;          /* index into array */

/* Agent and referer for logfiles */
char *header_user_agent;
char *header_referer;

int post_data_fd;           /* fd for post data tmpfile */

char *path_info;            /* env variable */
char *path_translated;      /* env variable */
char *script_name;          /* env variable */
char *query_string;         /* env variable */
char *content_type;         /* env variable */
char *content_length;       /* env variable */

struct mmap_entry *mmap_entry_var;

struct request *next;       /* next */
struct request *prev;       /* previous */

/* everything below this line is kept regardless */
char buffer[BUFFER_SIZE + 1]; /* generic I/O buffer */
char request_uri[MAX_HEADER_LENGTH + 1]; /* uri */
char client_stream[CLIENT_STREAM_SIZE]; /* data from client - fit or be hosed */
char *cgi_env[CGI_ENV_MAX + 4];             /* CGI environment */

#ifdef ACCEPT_ON
char accept[MAX_ACCEPT_LENGTH]; /* Accept: fields */
#endif
};

typedef struct request request;


 

static void fdset_update(void)
{
request *current, *next;

for (current = request_block; current; current = next)
{
time_t time_since = current_time - current->time_last;
next = current->next;

/* hmm, what if we are in "the middle" of a request and not
* just waiting for a new one... perhaps check to see if anything
* has been read via header position, etc... */
if (current->kacount < ka_max && /* we *are* in a keepalive */
(time_since >= ka_timeout) && /* ka timeout */
!current->logline) /* haven't read anything yet */
current->status = DEAD; /* connection keepalive timed out */
else if (time_since > REQUEST_TIMEOUT)
{
log_error_doc(current);
fputs("connection timed out\n", stderr);
current->status = DEAD;
}
if (current->buffer_end && current->status < DEAD)
{
if (FD_ISSET(current->fd, &block_write_fdset))
ready_request(current);
else
{
BOA_FD_SET(current->fd, &block_write_fdset);
}
} else
{
switch (current->status)
{
case WRITE:
case PIPE_WRITE:
if (FD_ISSET(current->fd, &block_write_fdset))
ready_request(current);
else
{
BOA_FD_SET(current->fd, &block_write_fdset);
}
break;
case BODY_WRITE:
if (FD_ISSET(current->post_data_fd, &block_write_fdset))
ready_request(current);
else
{
BOA_FD_SET(current->post_data_fd, &block_write_fdset);
}
break;
case PIPE_READ:
if (FD_ISSET(current->data_fd, &block_read_fdset))
ready_request(current);
else
{
BOA_FD_SET(current->data_fd, &block_read_fdset);
}
break;
case DONE:
if (FD_ISSET(current->fd, &block_write_fdset))
ready_request(current);
else
{
BOA_FD_SET(current->fd, &block_write_fdset);
}
break;
case DEAD:
ready_request(current);
break;
default:
if (FD_ISSET(current->fd, &block_read_fdset))
ready_request(current);
else
{
BOA_FD_SET(current->fd, &block_read_fdset);
}
break;
}
}
current = next;
}
}


for循环里面,

首先,获取time_since为距离上次成功操作经历的时间。

如果请求出于keepalive中,time_since已经大于ka_timeout(配置文件里可以配置),而且还没有读取到任何东西,那么request的status变为DEAD。

如果time_since大于REQUEST_TIMEOUT(60),那么status变为DEAD。

如果缓冲区有数据,而且status小于DEAD:

        如果不在block_write_fdset里,那么放到block_write_fdset里。

        如果fd已经在block_write_fdset里,调用ready_request,将request从block队列里转移到ready队列里,同时清除block_write_fdset里的标志

ready_request函数的功能是根据status,从fdset中清除对应fd。

其他情况:

        状态为WRITE,PIPE_WRITE,DONE的请求,如果没有那就放到block_write_fdset里,如果已经在了就调用ready_request。

        状态为BODY_WRITE,将request的post_data_fd做以上处理。post_data_fd注释为/* fd for post data tmpfile */,应该是客户端POST方法时的临时文件        

        状态为PIPE_READ,将request的data_fd做类似处理,不过检查的是block_read_fdset。

        状态为DEAD,直接调用ready_request。

        其他的,检查fd是否在block_read_fdset,并作相应处理。

 

 二、再看看process_erquests函数。

void process_requests(int server_s)
{
int retval = 0;
request *current, *trailer;

if (pending_requests) {
get_request(server_s);
#ifdef ORIGINAL_BEHAVIOR
pending_requests = 0;
#endif
}

current = request_ready;

while (current) {
time(¤t_time);
if (current->buffer_end && /* there is data in the buffer */
current->status != DEAD && current->status != DONE) {
retval = req_flush(current);
/*
* retval can be -2=error, -1=blocked, or bytes left
*/
if (retval == -2) { /* error */
current->status = DEAD;
retval = 0;
} else if (retval >= 0) {
/* notice the >= which is different from below?
Here, we may just be flushing headers.
We don't want to return 0 because we are not DONE
or DEAD */

retval = 1;
}
} else {
switch (current->status) {
case READ_HEADER:
case ONE_CR:
case ONE_LF:
case TWO_CR:
retval = read_header(current);
break;
case BODY_READ:
retval = read_body(current);
break;
case BODY_WRITE:
retval = write_body(current);
break;
case WRITE:
retval = process_get(current);
break;
case PIPE_READ:
retval = read_from_pipe(current);
break;
case PIPE_WRITE:
retval = write_from_pipe(current);
break;
case DONE:
/* a non-status that will terminate the request */
retval = req_flush(current);
/*
* retval can be -2=error, -1=blocked, or bytes left
*/
if (retval == -2) { /* error */
current->status = DEAD;
retval = 0;
} else if (retval > 0) {
retval = 1;
}
break;
case DEAD:
retval = 0;
current->buffer_end = 0;
SQUASH_KA(current);
break;
default:
retval = 0;
fprintf(stderr, "Unknown status (%d), "
"closing!\n", current->status);
current->status = DEAD;
break;
}

}

if (sigterm_flag)
SQUASH_KA(current);

/* we put this here instead of after the switch so that
* if we are on the last request, and get_request is successful,
* current->next is valid!
*/
if (pending_requests)
get_request(server_s);

switch (retval) {
case -1:               /* request blocked */
trailer = current;
current = current->next;
block_request(trailer);
break;
case 0:                /* request complete */
current->time_last = current_time;
trailer = current;
current = current->next;
free_request(&request_ready, trailer);
break;
case 1:                /* more to do */
current->time_last = current_time;
current = current->next;
break;
default:
log_error_time();
fprintf(stderr, "Unknown retval in process.c - "
"Status: %d, retval: %d\n", current->status, retval);
current = current->next;
break;
}
}
}

 

对于每一个ready queue里的请求遍历处理,返回值-1表示需要进入block queue;返回值0表示请求结束;返回值1表示还要在ready queue里,需进一步处理。

首先检查是否有pending_requests,如果有调用get_request(server_s);,接受一个connection,加入ready_queue。

get_request(server_s);大体功能是,接受一个请求,并做一些简单的初始化,加入ready_queue。

然后开始轮询ready链表:

如果有数据要写,状态不是DEAD或DONE,就调用req_flush(current)。

每一轮最后检查一次,是否还有pending_requests。有的话加入ready_queue。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: