本文主要是介绍boa源码分析(3)--代码结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- 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)--代码结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!