您好,欢迎来到爱玩科技网。
搜索
您的当前位置:首页BOA代码笔记4

BOA代码笔记4

来源:爱玩科技网




BOA代码笔记4

main.c(完?)从上次继续 上次我们看到了这个地方:

[cpp]view plaincopyprint?if (max_connections < 1)

structrlimit 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; case0:

/*child, success */ break; default:

/*parent, success */ exit(0);

break; /*main loop */

timestamp(); status.requests= 0;

status.errors= 0; start_time= current_time;

select_loop(server_s); return0;



第一个if块确定最大连接数。如果未指定,使用getrlimit来获得。

getrlimit(RLIMIT_NOFILE, &rl);

specifiesa value

one greater than the maximum file descriptor number that can be opened by this process. 第二个 if 块,如果 do_fork 1 ,那么我们从子进程运行,父进程结束。 timestamp 就 是一段小程序:

[cpp] view plaincopyprint?void timestamp(void)
{ log_error_time(); fprintf(stderr, "boa: server version %s\n", SERVER_VERSION);

log_error_time();

fprintf(stderr,"boa: server built "

__DATE__ " at " __TIME__ ".\n"); log_error_time();

fprintf(stderr, "boa: starting server pid=%d, port %d\n", (int)

getpid(), server_port); }

程序之前好像已经将 stderr 重定向到自定义的错误处理文件 中

了。然后,清空 status 的两个域,记录一下 start_time 。 然后就

开始select_loop 了?select_loop()ps :博主没啥经验, 并未通读过

源码,这也是第一次看正经点儿的源代码。这篇



写完最后仍然云里雾里,较多猜测。如有错误欢迎指正。如果想知道代码背后的思想流程架构,那就得等博主看完全部代码再总结了(如果我有这能力和耐心的话……)。

ps2:如果由于博主没有全局把握,看到哪儿讲哪儿的方式引起你的极度不适,我表示非常抱歉。并强烈推荐你自己去看源码,也许效果拔群。 :先粘一下select_loop的代码:

[cpp]view plaincopyprint?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)
} } /* reset max_fd */ max_fd = -1; if (request_block)



{ sigterm_stage2_run();
/*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) {
thing to do here on EBADF */ EINTR)

/* what is the appropriate if (errno =

continue;

/* while(1) */

elseif (errno != EBADF)

{ DIE("select");

}




if

(FD_ISSET(server_s,&block_read_fdset)) pending_requests = 1; }}

清空block_read_fdsetblock_write_fdset,这两个看名字我猜是用在 select里,具体用来表示哪些fd的集合以后才知道。

req_timeout用作select的时间,#define
REQUEST_TIMEOUT60 max_fd 置为-1然后进入了while(1)循环。首先是检测这么几个 flagsighup_flagsigchld_flag sigalrm_flagsigterm_flagboa的信号处理回头看一下 boa对信号的处理策略(voidinit_signals() )boa处理10个信号,其中 SIGPIPESIGUSR1SIGUSR2忽略掉。

SIGSEGVSIGBUSSIGTERMSIGHUPSIGINTSIGCHLDSIGALRM有自己的信号处理函数。对于段错误SIGSEGV,记录一下出错时间写到日志里,然后就abort了。毕

竟无法恢复。

对于SIGBUS,在另外两处视情况可能要好好的处理SIGBUS,之后讲到再说。默认情况下像SIGSEGV一样,也是记录一下, abort掉。

SIGBUS这个信号,印象中在 mmap后错误访问时会产

生,百度一下发现,在一些体系结构上,访问未对齐的地址会产生。



对于SIGINT,收到这个信号时,记录一下,正常退出。这个信号可以由 ctrl+c发送给foregroundprocess 产生。剩下了这四个在 while循环里处理的信号。

SIGHUP用来重新读取config_file。先清空fdset,清空
read_config_file里动态分配的内存,清空 request_free链表,然后调用 read_config_file

对于SIGCHLD的处理是典型的子进程处理方式,UNP里有总

结,如下:

[cpp]view plaincopyprint?sigchld_flag = 0; while ((pid = waitpid(-1,&status, WNOHANG)) > 0)

if (verbose_cgi_logs) { log_error_time();

time(Ot_time);
fprintf(stderr,"reaping

child %d: status %d\n", (int) pid, status); } return;

SIGALRM 只用来将 mime_hashtable passwd_hashtable 里的

数据写到日志文件里。

SIGTERM 两种处理方式
sigterm_stage1_run ,记录一下时间, 清空 block_read_set , 关掉 server_s ,意味着不再接受新的连接。然后设置 sigterm_flag = 2; 下一次由 sigterm_stage2_run 来处理。



sigterm_stage2_run,里完成正常结束的第二阶段:clear_common_env();dump_mime(); dump_passwd(); dump_alias(); free_requests(); exit(0)

SIGTERM通过两个函数使程序适当的中断。fd_update()信号处

理部分结束。

之后到达这么一段:

if(request_block)

/*move selected req's from request_block to request_ready */

fdset_update();源代码里对函数fdset_update();

说明如下:

/*

Name:fdset_update



*Description: iterate through the blocked requests, checking whether

*that file descriptor has been set by select. Updatethe fd_set to

*reflect current status.

*Here, we need to do some things:

*- keepalive timeouts simply close

*(this is special:: a keepalive timeout is a timeout where

keepaliveis active but nothing has been read yet)

*- regular timeouts close + error

-stuff in buffer and fd ready? writeit out



-fd ready for other actions? dothem

*/一句话总结:fdset_update将合适的requestblock链表

里移动到ready链表里。

boa里边有三个请求链表

request *request_ready = NULL;

request *request_block = NULL;

request *request_free = NULL; 连接需要 reqeust 结构体时优先从 果为空,将 malloc 一个 reqeust
fdset_update 进行如下处理: 首

/*ready list head */

/*blocked list head */

/*free list head */ 新的

request_free中提取。如简

单的描述一下,

先, 获取 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,将requestblock队列里转移到ready队列里,同时清除block_write_fdset

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

其他情况:
状态为WRITEPIPE_WRITEDONE的请求,如果没有那就放到 block_write_fdset里,如果已经在了就调 用 ready_request

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

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

状态为DEAD,直接调用ready_request。其他的,检查 fd是否在block_read_fdset,并作相应处理。这块儿目前看

代码只能知道这么做,但不明白作者背后的思想,模型。先慢慢

来,整体看完一遍后应该能更好
了解。process_requests()之后是process_requests(),按注释来看,功能与之前的 fdset_update()正好相反,将适合的 reqeustready链表移动到block链表。process_requests()



释如下:

/*

*Name: process_requests

*Description: Iterates through the ready queue, passing each

request

*to the appropriate handler for processing. It monitors the

*return value from handler functions, all of which return -1

*to indicate a block, 0 on completion and 1 to remain on the

readylist for more procesing.

*/对于每一个readyqueue 里的请求遍历处理,返回值-1表示需

要进入blockqueue ;返回值0表示请求结束;返回值1表示还

要在readyqueue 里。

首先检查是否有pending_requests,如果有调用



get_request(server_s);,接受一个connection,加入ready_queue

get_request(server_s);其实比较复杂,这里先不详细说了。大体

功能是,接受一个请求,并做一些简单的初始化,加入
ready_queue。然后开始pollready 链表:如果有数据要写,状态不是 DEADDONEreq_flush。之所以特殊处理,因为返

回值特殊 -2=error,-1=blocked, or bytes left 。然后对retval做规范处理。其他状态处理如下:

[cpp]view plaincopyprint?switch (current->status) { caseREAD_HEADER:

case ONE_CR: case TWO_CR:

read_header(current); case BODY_READ: read_body(current);

case ONE_LF:
retval =

break;
retval =
break;





case BODY_WRITE:

write_body(current); case WRITE:

process_get(current); case PIPE_READ:

read_from_pipe(current); case PIPE_WRITE:

write_from_pipe(current); case DONE:

terminate the request */

req_flush(current);

retval =

break;
retval =

break;
retval =

break;
retval =

break;
/* a non-status that will

retval =

/*

*retval can be -2=error, -1=blocked, or bytes left

*/

if (retval == 2) { /* error */

retval

current->status = DEAD;

= 0;

} else if (retval > 0)

1;} { retval = 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;}

每个状态的处理函数可能有自己的返回值,个别的需要规范化处

理,以尽量满足:

返回值-1表示需要进入blockqueue ;返回值0表示请求结束;返回值1表示还要在readyqueue 里。最后总的处理 retval

[cpp]view plaincopyprint?if (pending_requests)

get_request(server_s); switch(retval)

{ case -1: trailer =

/*request blocked */ 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: current->time_last = current_time;

/*more to do */

current = current->next; default:

break; log_error_time();

fprintf(stderr, "Unknown retval in process.c - "
"Status: %d, retval: %d\n", current->status, retval); current = current->next;
break; }

每一轮最后检查一次,是否还有 pending_requests 。有的话 加入 ready_queue 。这次先到这儿,看起来还是挺头晕的。 留下 get_requests 没说,以后如果再碰到再说。

Copyright © 2019- aiwanbo.com 版权所有 赣ICP备2024042808号-3

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务