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

2024-02-15 00:58
文章标签 分析 代码 源码 结构 boa

本文主要是介绍boa源码分析(3)--代码结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[cpp]  view plain copy
  1. 1  boa.c  
  2.   
  3. 主程序:  
  4.   
  5. ----1)  关闭文件     
  6. for(i=3;i<=1024;i++)    
  7.       close(i);  
  8.   
  9. ----2)  设置进程权限掩码   
  10. umask(~0600);    rw- --- ---;  
  11.   
  12. ----3)  打开黑洞,并将标准输入输出指向它,    
  13. open("/dev/null", 0);  
  14. dup2(devnullfd, STDIN_FILENO);  
  15. dup2(devnullfd, STDOUT_FILENO);  
  16.   
  17.  ----4)  更新时间戳,日志要用到。  
  18. time(¤t_time);  
  19.   
  20. ----5)解析命令行  
  21. -f  
  22. server_root = strdup(optaarg);  
  23. -r    
  24. chdir(optarg);     
  25. chroot(optarg);  
  26. chdir("/");  
  27. -d   
  28. do_fork=0;  
  29.   
  30. ----6)//确保服务器根目录是有效的。  
  31. fixup_server_root();  
  32.   
  33. ----7)//读取配置文件通过yyparse 确保必要的变量设置正确。  
  34. read_config_files();  
  35.   
  36. ----8)//打开access log,error log, [cgi log] ;并设置 close-on-exec为真,即在exec调用后,关闭文件描述符。  
  37. open_logs();  
  38.   
  39. ----9)//创建TCP socket,设置为nonblock ,同样设置 close-on-exec为真,这样,EXEC调用时,cgi不能向它写入。。。  
  40. server_s = create_server_socket();    //打开了地址复用功能 详见 unix网络编程。  
  41.   
  42. ---10)//指定各信号的handle  
  43. init_signals();  
  44.   
  45. ---11)//设置用户ID和进程组ID。  
  46. drop_privs();//降 特权  
  47.   
  48. ---12) Set up the environment variables that are common to all CGI scripts  
  49.  create_common_env();  
  50.   
  51. ---13)  fork子进程,父进程退出。之后子进程成为守护进程  
  52. if(do_fork)   switch(fork())  
  53.   
  54. ---14) 得到PID,用于产生独一无二的临时文件名或路径。  
  55. int pid = getpid();  
  56.   
  57. ---15)  更新时间戳,然后进入主循环。  
  58. timestamp();  
  59. select_loop(server_s)  
  60. {  
  61.   1)清空,block_read_fdset、block_write_fdset;  
  62.   2)设置server_s和请求超时时间。  
  63.   3)进入while(1)  
  64.   {  
  65.      1)   处理sighup 、  sigchld 、 sigalrm、 sigterm等信号。  
  66.       2)重设max_fd = -1;  
  67.      3)   将合适的request从block链表里移到ready链表里。  
  68.            if(reques_block)     fdset_update(); //  
  69.   
  70.      4)  process_requests(server_s);  
  71.   
  72.      5)  if (!sigterm_flag && total_connections < (max_connections - 10))          BOA_FD_SET(server_s, &block_read_fdset); /* server always set */  
  73.   
  74.     6)   reset  timeout  
  75.   
  76.      7)   select调用,select(max_fd + 1, &block_read_fdset, &block_write_fdset, NULL, (request_ready || request_block ? &req_timeout : NULL))  
  77.   
  78.     8)更新当前时间,time(&curent_time);  
  79.     9)  if (FD_ISSET(server_s, &block_read_fdset))   pending_requests = 1;  
  80.   
  81.   }  
  82.   
  83. }  


 一、先来看看 fdset_update()

boa里边有三个请求链表

request *request_ready = NULL;  /* ready list head */
request *request_block = NULL;   /* blocked list head */
request *request_free = NULL;      /* free list head */

[cpp]  view plain copy
  1. struct request {                /* pending requests */  
  2.     int fd;                     /* client's socket fd */  
  3.     int status;                 /* see #defines.h */  
  4.     time_t time_last;           /* time of last succ. op. */  
  5.     char *pathname;             /* pathname of requested file */  
  6.     int simple;                 /* simple request? */  
  7.     int keepalive;              /* keepalive status */  
  8.     int kacount;                /* keepalive count */  
  9.   
  10.     int data_fd;                /* fd of data */  
  11.     unsigned long filesize;     /* filesize */  
  12.     unsigned long filepos;      /* position in file */  
  13.     char *data_mem;             /* mmapped/malloced char array */  
  14.     int method;                 /* M_GET, M_POST, etc. */  
  15.   
  16.     char *logline;              /* line to log file */  
  17.   
  18.     char *header_line;          /* beginning of un or incompletely processed header line */  
  19.     char *header_end;           /* last known end of header, or end of processed data */  
  20.     int parse_pos;              /* how much have we parsed */  
  21.     int client_stream_pos;      /* how much have we read... */  
  22.   
  23.     int buffer_start;           /* where the buffer starts */  
  24.     int buffer_end;             /* where the buffer ends */  
  25.   
  26.     char *http_version;         /* HTTP/?.? of req */  
  27.     int response_status;        /* R_NOT_FOUND etc. */  
  28.   
  29.     char *if_modified_since;    /* If-Modified-Since */  
  30.     time_t last_modified;       /* Last-modified: */  
  31.   
  32.     char local_ip_addr[NI_MAXHOST]; /* for virtualhost */  
  33.   
  34.     /* CGI vars */  
  35.   
  36.     int remote_port;            /* could be used for ident */  
  37.   
  38.     char remote_ip_addr[NI_MAXHOST]; /* after inet_ntoa */  
  39.   
  40.     int is_cgi;                 /* true if CGI/NPH */  
  41.     int cgi_status;  
  42.     int cgi_env_index;          /* index into array */  
  43.   
  44.     /* Agent and referer for logfiles */  
  45.     char *header_user_agent;  
  46.     char *header_referer;  
  47.   
  48.     int post_data_fd;           /* fd for post data tmpfile */  
  49.   
  50.     char *path_info;            /* env variable */  
  51.     char *path_translated;      /* env variable */  
  52.     char *script_name;          /* env variable */  
  53.     char *query_string;         /* env variable */  
  54.     char *content_type;         /* env variable */  
  55.     char *content_length;       /* env variable */  
  56.   
  57.     struct mmap_entry *mmap_entry_var;  
  58.   
  59.     struct request *next;       /* next */  
  60.     struct request *prev;       /* previous */  
  61.   
  62.     /* everything below this line is kept regardless */  
  63.     char buffer[BUFFER_SIZE + 1]; /* generic I/O buffer */  
  64.     char request_uri[MAX_HEADER_LENGTH + 1]; /* uri */  
  65.     char client_stream[CLIENT_STREAM_SIZE]; /* data from client - fit or be hosed */  
  66.     char *cgi_env[CGI_ENV_MAX + 4];             /* CGI environment */  
  67.   
  68. #ifdef ACCEPT_ON  
  69.     char accept[MAX_ACCEPT_LENGTH]; /* Accept: fields */  
  70. #endif  
  71. };  
  72.   
  73. typedef struct request request;  


 

[cpp]  view plain copy
  1. static void fdset_update(void)  
  2. {  
  3.     request *current, *next;  
  4.   
  5.     for (current = request_block; current; current = next)  
  6.     {  
  7.         time_t time_since = current_time - current->time_last;  
  8.         next = current->next;  
  9.           
  10.         /* hmm, what if we are in "the middle" of a request and not 
  11.          * just waiting for a new one... perhaps check to see if anything 
  12.          * has been read via header position, etc... */  
  13.         if (current->kacount < ka_max && /* we *are* in a keepalive */  
  14.         (time_since >= ka_timeout) && /* ka timeout */  
  15.         !current->logline) /* haven't read anything yet */  
  16.             current->status = DEAD; /* connection keepalive timed out */  
  17.         else if (time_since > REQUEST_TIMEOUT)  
  18.         {  
  19.             log_error_doc(current);  
  20.             fputs("connection timed out\n", stderr);  
  21.             current->status = DEAD;  
  22.         }  
  23.         if (current->buffer_end && current->status < DEAD)  
  24.         {  
  25.             if (FD_ISSET(current->fd, &block_write_fdset))  
  26.                 ready_request(current);  
  27.             else  
  28.             {  
  29.                 BOA_FD_SET(current->fd, &block_write_fdset);  
  30.             }  
  31.         } else  
  32.         {  
  33.             switch (current->status)  
  34.             {  
  35.             case WRITE:  
  36.             case PIPE_WRITE:  
  37.                 if (FD_ISSET(current->fd, &block_write_fdset))  
  38.                     ready_request(current);  
  39.                 else  
  40.                 {  
  41.                     BOA_FD_SET(current->fd, &block_write_fdset);  
  42.                 }  
  43.                 break;  
  44.             case BODY_WRITE:  
  45.                 if (FD_ISSET(current->post_data_fd, &block_write_fdset))  
  46.                     ready_request(current);  
  47.                 else  
  48.                 {  
  49.                     BOA_FD_SET(current->post_data_fd, &block_write_fdset);  
  50.                 }  
  51.                 break;  
  52.             case PIPE_READ:  
  53.                 if (FD_ISSET(current->data_fd, &block_read_fdset))  
  54.                     ready_request(current);  
  55.                 else  
  56.                 {  
  57.                     BOA_FD_SET(current->data_fd, &block_read_fdset);  
  58.                 }  
  59.                 break;  
  60.             case DONE:  
  61.                 if (FD_ISSET(current->fd, &block_write_fdset))  
  62.                     ready_request(current);  
  63.                 else  
  64.                 {  
  65.                     BOA_FD_SET(current->fd, &block_write_fdset);  
  66.                 }  
  67.                 break;  
  68.             case DEAD:  
  69.                 ready_request(current);  
  70.                 break;  
  71.             default:  
  72.                 if (FD_ISSET(current->fd, &block_read_fdset))  
  73.                     ready_request(current);  
  74.                 else  
  75.                 {  
  76.                     BOA_FD_SET(current->fd, &block_read_fdset);  
  77.                 }  
  78.                 break;  
  79.             }  
  80.         }  
  81.         current = next;  
  82.     }  
  83. }  


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函数。

[cpp]  view plain copy
  1. void process_requests(int server_s)  
  2. {  
  3.     int retval = 0;  
  4.     request *current, *trailer;  
  5.   
  6.     if (pending_requests) {  
  7.         get_request(server_s);  
  8. #ifdef ORIGINAL_BEHAVIOR  
  9.         pending_requests = 0;  
  10. #endif  
  11.     }  
  12.   
  13.     current = request_ready;  
  14.   
  15.     while (current) {  
  16.         time(¤t_time);  
  17.         if (current->buffer_end && /* there is data in the buffer */  
  18.             current->status != DEAD && current->status != DONE) {  
  19.             retval = req_flush(current);  
  20.             /* 
  21.              * retval can be -2=error, -1=blocked, or bytes left 
  22.              */  
  23.             if (retval == -2) { /* error */  
  24.                 current->status = DEAD;  
  25.                 retval = 0;  
  26.             } else if (retval >= 0) {  
  27.                 /* notice the >= which is different from below? 
  28.                    Here, we may just be flushing headers. 
  29.                    We don't want to return 0 because we are not DONE 
  30.                    or DEAD */  
  31.   
  32.                 retval = 1;  
  33.             }  
  34.         } else {  
  35.             switch (current->status) {  
  36.             case READ_HEADER:  
  37.             case ONE_CR:  
  38.             case ONE_LF:  
  39.             case TWO_CR:  
  40.                 retval = read_header(current);  
  41.                 break;  
  42.             case BODY_READ:  
  43.                 retval = read_body(current);  
  44.                 break;  
  45.             case BODY_WRITE:  
  46.                 retval = write_body(current);  
  47.                 break;  
  48.             case WRITE:  
  49.                 retval = process_get(current);  
  50.                 break;  
  51.             case PIPE_READ:  
  52.                 retval = read_from_pipe(current);  
  53.                 break;  
  54.             case PIPE_WRITE:  
  55.                 retval = write_from_pipe(current);  
  56.                 break;  
  57.             case DONE:  
  58.                 /* a non-status that will terminate the request */  
  59.                 retval = req_flush(current);  
  60.                 /* 
  61.                  * retval can be -2=error, -1=blocked, or bytes left 
  62.                  */  
  63.                 if (retval == -2) { /* error */  
  64.                     current->status = DEAD;  
  65.                     retval = 0;  
  66.                 } else if (retval > 0) {  
  67.                     retval = 1;  
  68.                 }  
  69.                 break;  
  70.             case DEAD:  
  71.                 retval = 0;  
  72.                 current->buffer_end = 0;  
  73.                 SQUASH_KA(current);  
  74.                 break;  
  75.             default:  
  76.                 retval = 0;  
  77.                 fprintf(stderr, "Unknown status (%d), "  
  78.                         "closing!\n", current->status);  
  79.                 current->status = DEAD;  
  80.                 break;  
  81.             }  
  82.   
  83.         }  
  84.   
  85.         if (sigterm_flag)  
  86.             SQUASH_KA(current);  
  87.   
  88.         /* we put this here instead of after the switch so that 
  89.          * if we are on the last request, and get_request is successful, 
  90.          * current->next is valid! 
  91.          */  
  92.         if (pending_requests)  
  93.             get_request(server_s);  
  94.   
  95.         switch (retval) {  
  96.         case -1:               /* request blocked */  
  97.             trailer = current;  
  98.             current = current->next;  
  99.             block_request(trailer);  
  100.             break;  
  101.         case 0:                /* request complete */  
  102.             current->time_last = current_time;  
  103.             trailer = current;  
  104.             current = current->next;  
  105.             free_request(&request_ready, trailer);  
  106.             break;  
  107.         case 1:                /* more to do */  
  108.             current->time_last = current_time;  
  109.             current = current->next;  
  110.             break;  
  111.         default:  
  112.             log_error_time();  
  113.             fprintf(stderr, "Unknown retval in process.c - "  
  114.                     "Status: %d, retval: %d\n", current->status, retval);  
  115.             current = current->next;  
  116.             break;  
  117.         }  
  118.     }  
  119. }  

 

对于每一个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)--代码结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/710045

相关文章

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

jupyter代码块没有运行图标的解决方案

《jupyter代码块没有运行图标的解决方案》:本文主要介绍jupyter代码块没有运行图标的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录jupyter代码块没有运行图标的解决1.找到Jupyter notebook的系统配置文件2.这时候一般会搜索到

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java