BOA代码笔记 4.docx

上传人:b****5 文档编号:29380621 上传时间:2023-07-22 格式:DOCX 页数:7 大小:18.73KB
下载 相关 举报
BOA代码笔记 4.docx_第1页
第1页 / 共7页
BOA代码笔记 4.docx_第2页
第2页 / 共7页
BOA代码笔记 4.docx_第3页
第3页 / 共7页
BOA代码笔记 4.docx_第4页
第4页 / 共7页
BOA代码笔记 4.docx_第5页
第5页 / 共7页
点击查看更多>>
下载资源
资源描述

BOA代码笔记 4.docx

《BOA代码笔记 4.docx》由会员分享,可在线阅读,更多相关《BOA代码笔记 4.docx(7页珍藏版)》请在冰豆网上搜索。

BOA代码笔记 4.docx

BOA代码笔记4

BOA代码笔记4

main.c(完?

从上次继续

上次我们看到了这个地方:

[cpp]viewplaincopyprint?

if(max_connections<1){structrlimitrl;/*hasnotbeensetexplicitly*/c=getrlimit(RLIMIT_NOFILE,&rl);if(c<0){perror("getrlimit");exit

(1);}max_connections=rl.rlim_cur;}/*backgroundourself*/if(do_fork){switch(fork()){case-1:

/*error*/perror("fork");exit

(1);break;case0:

/*child,success*/break;default:

/*parent,success*/exit(0);break;}}/*mainloop*/timestamp();status.requests=0;status.errors=0;start_time=current_time;select_loop(server_s);return0;

第一个if块确定最大连接数。

如果未指定,使用getrlimit来获得。

getrlimit(RLIMIT_NOFILE,&rl);specifiesavalueonegreaterthanthemaximumfiledescriptornumberthatcanbeopenedbythisprocess.第二个if块,如果do_fork为1,那么我们从子进程运行,父进程结束。

timestamp就是一段小程序:

[cpp]viewplaincopyprint?

voidtimestamp(void){log_error_time();fprintf(stderr,"boa:

serverversion%s\n",SERVER_VERSION);log_error_time();fprintf(stderr,"boa:

serverbuilt"__DATE__"at"__TIME__".\n");log_error_time();fprintf(stderr,"boa:

startingserverpid=%d,port%d\n",(int)getpid(),server_port);}

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

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

然后就开始select_loop了~select_loop()ps:

博主没啥经验,并未通读过源码,这也是第一次看正经点儿的源代码。

这篇写完最后仍然云里雾里,较多猜测。

如有错误欢迎指正。

如果想知道代码背后的思想流程架构,那就得等博主看完全部代码再总结了(如果我有这能力和耐心的话……)。

ps2:

如果由于博主没有全局把握,看到哪儿讲哪儿的方式引起你的极度不适,我表示非常抱歉。

并强烈推荐你自己去看源码,也许效果拔群。

:

先粘一下select_loop的代码:

[cpp]viewplaincopyprint?

voidselect_loop(intserver_s){FD_ZERO(&block_read_fdset);FD_ZERO(&block_write_fdset);/*setserver_sandreq_timeout*/req_timeout.tv_sec=(ka_timeout?

ka_timeout:

REQUEST_TIMEOUT);req_timeout.tv_usec=0l;/*resettimeout*//*presetmax_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();}}/*resetmax_fd*/max_fd=-1;if(request_block)/*moveselectedreq'sfromrequest_blocktorequest_ready*/fdset_update();/*anyblockedreq'smovefromrequest_readytorequest_block*/process_requests(server_s);if(!

sigterm_flag&&total_connections<(max_connections-10)){BOA_FD_SET(server_s,&block_read_fdset);/*serveralwaysset*/}req_timeout.tv_sec=(request_ready?

0:

(ka_timeout?

ka_timeout:

REQUEST_TIMEOUT));req_timeout.tv_usec=0l;/*resettimeout*/if(select(max_fd+1,&block_read_fdset,&block_write_fdset,NULL,(request_ready||request_block?

&req_timeout:

NULL))==-1){/*whatistheappropriatethingtodohereonEBADF*/if(errno==EINTR)continue;/*while

(1)*/elseif(errno!

=EBADF){DIE("select");}}time(¤t_time);if(FD_ISSET(server_s,&block_read_fdset))pending_requests=1;}}

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

req_timeout用作select的时间限制,#defineREQUEST_TIMEOUT60

max_fd置为-1然后进入了while

(1)循环。

首先是检测这么几个flag:

sighup_flag,sigchld_flag,sigalrm_flag,sigterm_flag。

boa的信号处理

回头看一下boa对信号的处理策略(voidinit_signals()中):

boa处理10个信号,其中SIGPIPE,SIGUSR1,SIGUSR2忽略掉。

SIGSEGV,SIGBUS,SIGTERM,SIGHUP,SIGINT,SIGCHLD,SIGALRM有自己的信号处理函数。

对于段错误SIGSEGV,记录一下出错时间写到日志里,然后就abort了。

毕竟无法恢复。

对于SIGBUS,在另外两处视情况可能要好好的处理SIGBUS,之后讲到再说。

默认情况下像SIGSEGV一样,也是记录一下,abort掉。

SIGBUS这个信号,印象中在mmap后错误访问时会产生,XX一下发现,在一些体系结构上,访问未对齐的地址会产生。

对于SIGINT,收到这个信号时,记录一下,正常退出。

这个信号可以由ctrl+c发送给foregroundprocess产生。

剩下了这四个在while循环里处理的信号。

SIGHUP用来重新读取config_file。

先清空fdset,清空read_config_file里动态分配的内存,清空request_free链表,然后调用read_config_file。

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

[cpp]viewplaincopyprint?

sigchld_flag=0;while((pid=waitpid(-1,&status,WNOHANG))>0)if(verbose_cgi_logs){time(¤t_time);log_error_time();fprintf(stderr,"reapingchild%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)

/*moveselectedreq'sfromrequest_blocktorequest_ready*/

fdset_update();源代码里对函数fdset_update();的说明如下:

/*

*Name:

fdset_update

*

*Description:

iteratethroughtheblockedrequests,checkingwhether

*thatfiledescriptorhasbeensetbyselect.Updatethefd_setto

*reflectcurrentstatus.

*

*Here,weneedtodosomethings:

*-keepalivetimeoutssimplyclose

*(thisisspecial:

:

akeepalivetimeoutisatimeoutwhere

keepaliveisactivebutnothinghasbeenreadyet)

*-regulartimeoutsclose+error

*-stuffinbufferandfdready?

writeitout

*-fdreadyforotheractions?

dothem

*/一句话总结:

fdset_update将合适的request从block链表里移动到ready链表里。

 

boa里边有三个请求链表

request*request_ready=NULL;/*readylisthead*/

request*request_block=NULL;/*blockedlisthead*/

request*request_free=NULL;/*freelisthead*/新的连接需要reqeust结构体时优先从request_free中提取。

如果为空,将malloc一个reqeust。

简单的描述一下,fdset_update进行如下处理:

首先,获取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注释为/*fdforpostdatatmpfile*/,应该是客户端POST方法时的临时文件,以后再详看。

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

状态为DEAD,直接调用ready_request。

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

这块儿目前看代码只能知道这么做,但不明白作者背后的思想,模型。

先慢慢来,整体看完一遍后应该能更好了解。

process_requests()之后是process_requests(),按注释来看,功能与之前的fdset_update()正好相反,将适合的reqeust从ready链表移动到block链表。

process_requests()注释如下:

/*

*Name:

process_requests

*

*Description:

Iteratesthroughthereadyqueue,passingeachrequest

*totheappropriatehandlerforprocessing.Itmonitorsthe

*returnvaluefromhandlerfunctions,allofwhichreturn-1

*toindicateablock,0oncompletionand1toremainonthe

*readylistformoreprocesing.

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

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

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

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

然后开始pollready链表:

如果有数据要写,状态不是DEAD或DONE,req_flush。

之所以特殊处理,因为返回值特殊-2=error,-1=blocked,orbytesleft。

然后对retval做规范处理。

其他状态处理如下:

[cpp]viewplaincopyprint?

switch(current->status){caseREAD_HEADER:

caseONE_CR:

caseONE_LF:

caseTWO_CR:

retval=read_header(current);break;caseBODY_READ:

retval=read_body(current);break;caseBODY_WRITE:

retval=write_body(current);break;caseWRITE:

retval=process_get(current);break;casePIPE_READ:

retval=read_from_pipe(current);break;casePIPE_WRITE:

retval=write_from_pipe(current);break;caseDONE:

/*anon-statusthatwillterminatetherequest*/retval=req_flush(current);/**retvalcanbe-2=error,-1=blocked,orbytesleft*/if(retval==-2){/*error*/current->status=DEAD;retval=0;}elseif(retval>0){retval=1;}break;caseDEAD:

retval=0;current->buffer_end=0;SQUASH_KA(current);break;default:

retval=0;fprintf(stderr,"Unknownstatus(%d),""closing!

\n",current->status);current->status=DEAD;break;}

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

返回值-1表示需要进入blockqueue;返回值0表示请求结束;返回值1表示还要在readyqueue里。

最后总的处理retval:

[cpp]viewplaincopyprint?

if(pending_requests)get_request(server_s);switch(retval){case-1:

/*requestblocked*/trailer=current;current=current->next;block_request(trailer);break;case0:

/*requestcomplete*/current->time_last=current_time;trailer=current;current=current->next;free_request(&request_ready,trailer);break;case1:

/*moretodo*/current->time_last=current_time;current=current->next;break;default:

log_error_time();fprintf(stderr,"Unknownretvalinprocess.c-""Status:

%d,retval:

%d\n",current->status,retval);current=current->next;break;}

每一轮最后检查一次,是否还有pending_requests。

有的话加入ready_queue。

这次先到这儿,看起来还是挺头晕的。

留下get_requests没说,以后如果再碰到再说。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1