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。
相关文章推荐
- boa源码分析(3)--代码结构
- boa源码分析(3)--代码结构
- OpenERP源码结构分析及RPC相关代码分布
- LIRE原理与源码分析(一)——代码结构
- sqoop 抽取源码流程分析( 二 ) 导入工具代码结构分析
- MySQL源码分析——代码结构与基本流程
- Uboot 源码分析----代码整体结构分析
- libevent源码分析--代码结构
- EntLib.com Forum / YAF 开源论坛--源码的目录结构(对分析代码很有帮助)
- nsq源码分析(1):代码结构
- FreeRTOS源码分析-代码结构
- Flume源码分析—代码结构理解(四)
- jquery源码解析:代码结构分析
- Uboot 源码分析----代码整体结构分析
- 开放源代码的全文检索引擎Lucene――介绍、系统结构与源码实现分析
- C#分析数据库结构,使用XSL模板自动生成代码 - 清清月儿 .NET万花筒 Asp.net技术 Asp.net教程 Asp.net源码 Asp.net基础 Asp.net控件 Asp.net入门 - CSDNBlog
- C++卷积神经网络实例:tiny_cnn代码详解(11)——层结构容器layers类源码分析
- Heritrix源码分析(六) Heritrix的文件结构分析(转)
- Dubbo系列(七)Dubbo源码分析之结构描述
- boa源码分析(1)--Makefile