apr_pcalloc

APR_DECLARE(void *) apr_palloc(apr_pool_t *pool, apr_size_t in_size)
{
    将in_size调整为index整数倍的
    /*先查看active节点,active是内存池中的激活节点,所有的内存都从这个节点中分配*/
    if(active节点就能够满足分配要求)
    {
        直接从active分配内存
        返回了
        /*这里不会往下执行,直接返回了,也就不会理睬在active分配内存之后,还是不是链表中的最大

节点,所以tingya的对这个地方的解释不完全正确,应该是active节点是不会理睬有多大的,反正所有的内存都从

active节点分配,当active节点不够分配时,就更换active节点.从active->next开始的链表,是按照可用空间从大

到小排序的*/
    }
    查看active->next节点/*是链表中最大的节点,如果这个节点都不够分,就只能从分配子中提取节点了*/
    if(active->next节点能够满足要求)
    {
        把这个节点从链表中提取出来,用node表示;
    }
    else
    {
        从分配子从获取节点,用node表示;
    }
    
    /*此时内存已经分配出来了,并且是从node节点中分配的*/
    /*所以接下来我要用node把active替换掉,让active回到链表中,并且是回到排序后的位置上,看代码:*/
    
    把node放到active链表头,即挤掉原active的位置
    用active的可用大小去和链表后面的节点依次做比较,插入到合适的位置.
    /*因为每次我都做的是相同的操作,所以我不需要再对整个链表进行排序就可以保证链表是从大到小排序

的*/
    返回内存起始地址;
}

2011 - 04 - 16

apr_strings.c

C C++ C#
char* apr_pstrdup(apr_pool_t * p,
  const char * s 
 )

duplicate a string into memory allocated out of a pool

Parameters:
 p The pool to allocate out of
 s The string to duplicate
Returns:
The new string

 

2011 - 04 - 09

Apache Handler

       A "handler" is an internal Apache representation of the action to be performed when a file is called. Generally, files have implicit handlers, based on the file type. Normally, all files are simply served by the server, but certain file types are "handled" separately.

 

      Apache 1.1 adds the ability to use handlers explicitly. Based on either filename extensions or on location, handlers can be specified without relation to file type. This is advantageous both because it is a more elegant solution, and because it also allows for both a type and a handler to be associated with a file.

      Handlers can either be built into the server or included in a module, or they can be added with the Action directive. The built-in handlers in the standard distribution are as follows:

     

  • default-handler: Send the file using the default_handler(), which is the handler used by default to handle static content. (core)
  • send-as-is: Send file with HTTP headers as is. (mod_asis)
  • cgi-script: Treat the file as a CGI script. (mod_cgi)
  • imap-file: Parse as an imagemap rule file. (mod_imap)
  • server-info: Get the server's configuration information. (mod_info)
  • server-status: Get the server's status report. (mod_status)
  • type-map: Parse as a type map file for content negotiation. (mod_negotiation)

 

变量:

process_rec,a structure that represents one process。

 

struct process_rec {apr_pool_t *pool; //全局池apr_pool_t *pconf; //配置池int argc; //参数个数const char * const *argv; //参数const char *short_name;};//表示进程的数据结构

 

apr_pool_t:基本池类型

 

1: AP_MONCONTROL(0); 代码剖析控制

 

2: process = init_process(&argc, &argc); //进程初始化,process代表主进程信息。

 

3: apr_pool_create(&pcommands, pglobal);

      apr_pool_tag(pcommands, "pcommands");

      pcommands --> 命令行参数池

 

4: error = ap_setup_prelinked_modules(process); //将所有预连接的模块加入到加载模块链表中。

 

5: ap_run_rewrite_args(process);

rewrite_args是为MPM模块设置的,允许MPM模块对命令行中的传入参数进行重写。

 

6: 参数处理

apr_getopt_t *opt;

apr_getopt_init(&opt, pcommands, process->argc, process->argv);

处理参数传入opt。

while((rv=apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg)) == APR_SUCCESS) {

char **new;

switch(c) {

case 'c':

}

}

 

7: apr_pool_create(&plog, pglobal);

apr_pool_tag(plog, "plog");

内存池,内存池树

 

8: ap_replace_stderr_log(process->pool, temp_error_log);

输出日志文件替换。

 

9: 读取配置

server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);

解析成配置树。

 

10: ap_run_pre_config(pconf, plog, ptemp);

触发模块的pre_config挂钩,是模块可以修改配置树。

 

11: ap_process_config_tree

处理配置树。

 

12: signal_server = APR_RETRIEVE_OPTIONAL_FN(ap_signal_server);

ap_run_post_config(pconf, plog, ptemp, server_conf);

 

13: for(;;) 主循环

请求处理被分割为多个挂钩阶段,每个阶段实现一个特定的任务,开发人员注册挂钩。Apache会自动执行所有挂钩函数。

 

 

2011 - 02 - 25

Apache基本概念

 

APR: Apache Portable Runtime, Apache可移植运行库,屏蔽操作系统底层细节,开发人员关注功能。

 

MPM: Multi-Processes Modules, 多进程处理模块,充分利用操作系统的特性(不存在适用所有操作系统的并发模 型)。

 

 

2011 - 02 - 25

Apache main函数源代码

 

 

 

C代码 
  1. #include "apr.h"  
  2. #include "apr_strings.h"  
  3. #include "apr_getopt.h"  
  4. #include "apr_general.h"  
  5. #include "apr_lib.h"  
  6. #include "apr_md5.h"  
  7. #include "apr_time.h"  
  8. #include "apr_version.h"  
  9. #include "apu_version.h"  
  10.   
  11. #define APR_WANT_STDIO  
  12. #define APR_WANT_STRFUNC  
  13. #include "apr_want.h"  
  14.   
  15. #define CORE_PRIVATE  
  16. #include "ap_config.h"  
  17. #include "httpd.h"  
  18. #include "http_main.h"  
  19. #include "http_log.h"  
  20. #include "http_config.h"  
  21. #include "http_core.h"  
  22. #include "http_vhost.h"  
  23. #include "apr_uri.h"  
  24. #include "util_ebcdic.h"  
  25. #include "ap_mpm.h"  
  26. #include "mpm_common.h"  
  27.   
  28. /* WARNING: Win32 binds http_main.c dynamically to the server. Please place 
  29.  *          extern functions and global data in another appropriate module. 
  30.  * 
  31.  * Most significant main() global data can be found in http_config.c 
  32.  */  
  33.   
  34. static void show_mpm_settings(void)  
  35. {  
  36.     int mpm_query_info;  
  37.     apr_status_t retval;  
  38.   
  39.     printf("Server MPM:     %s\n", ap_show_mpm());  
  40.   
  41.     retval = ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_query_info);  
  42.   
  43.     if (retval == APR_SUCCESS) {  
  44.         printf("  threaded:     ");  
  45.   
  46.         if (mpm_query_info == AP_MPMQ_DYNAMIC) {  
  47.             printf("yes (variable thread count)\n");  
  48.         }  
  49.         else if (mpm_query_info == AP_MPMQ_STATIC) {  
  50.             printf("yes (fixed thread count)\n");  
  51.         }  
  52.         else {  
  53.             printf("no\n");  
  54.         }  
  55.     }  
  56.   
  57.     retval = ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_query_info);  
  58.   
  59.     if (retval == APR_SUCCESS) {  
  60.         printf("    forked:     ");  
  61.   
  62.         if (mpm_query_info == AP_MPMQ_DYNAMIC) {  
  63.             printf("yes (variable process count)\n");  
  64.         }  
  65.         else if (mpm_query_info == AP_MPMQ_STATIC) {  
  66.             printf("yes (fixed process count)\n");  
  67.         }  
  68.         else {  
  69.             printf("no\n");  
  70.         }  
  71.     }  
  72. }  
  73.   
  74. static void show_compile_settings(void)  
  75. {  
  76.     printf("Server version: %s\n", ap_get_server_description());  
  77.     printf("Server built:   %s\n", ap_get_server_built());  
  78.     printf("Server's Module Magic Number: %u:%u\n",  
  79.            MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);  
  80.     printf("Server loaded:  APR %s, APR-Util %s\n",  
  81.            apr_version_string(), apu_version_string());  
  82.     printf("Compiled using: APR %s, APR-Util %s\n",  
  83.            APR_VERSION_STRING, APU_VERSION_STRING);  
  84.     /* sizeof(foo) is long on some platforms so we might as well 
  85.      * make it long everywhere to keep the printf format 
  86.      * consistent 
  87.      */  
  88.     printf("Architecture:   %ld-bit\n", 8 * (long)sizeof(void *));  
  89.   
  90.     show_mpm_settings();  
  91.   
  92.     printf("Server compiled with....\n");  
  93. #ifdef BIG_SECURITY_HOLE  
  94.     printf(" -D BIG_SECURITY_HOLE\n");  
  95. #endif  
  96.   
  97. #ifdef SECURITY_HOLE_PASS_AUTHORIZATION  
  98.     printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n");  
  99. #endif  
  100.   
  101. #ifdef OS  
  102.     printf(" -D OS=\"" OS "\"\n");  
  103. #endif  
  104.   
  105. #ifdef APACHE_MPM_DIR  
  106.     printf(" -D APACHE_MPM_DIR=\"" APACHE_MPM_DIR "\"\n");  
  107. #endif  
  108.   
  109. #ifdef HAVE_SHMGET  
  110.     printf(" -D HAVE_SHMGET\n");  
  111. #endif  
  112.   
  113. #if APR_FILE_BASED_SHM  
  114.     printf(" -D APR_FILE_BASED_SHM\n");  
  115. #endif  
  116.   
  117. #if APR_HAS_SENDFILE  
  118.     printf(" -D APR_HAS_SENDFILE\n");  
  119. #endif  
  120.   
  121. #if APR_HAS_MMAP  
  122.     printf(" -D APR_HAS_MMAP\n");  
  123. #endif  
  124.   
  125. #ifdef NO_WRITEV  
  126.     printf(" -D NO_WRITEV\n");  
  127. #endif  
  128.   
  129. #ifdef NO_LINGCLOSE  
  130.     printf(" -D NO_LINGCLOSE\n");  
  131. #endif  
  132.   
  133. #if APR_HAVE_IPV6  
  134.     printf(" -D APR_HAVE_IPV6 (IPv4-mapped addresses ");  
  135. #ifdef AP_ENABLE_V4_MAPPED  
  136.     printf("enabled)\n");  
  137. #else  
  138.     printf("disabled)\n");  
  139. #endif  
  140. #endif  
  141.   
  142. #if APR_USE_FLOCK_SERIALIZE  
  143.     printf(" -D APR_USE_FLOCK_SERIALIZE\n");  
  144. #endif  
  145.   
  146. #if APR_USE_SYSVSEM_SERIALIZE  
  147.     printf(" -D APR_USE_SYSVSEM_SERIALIZE\n");  
  148. #endif  
  149.   
  150. #if APR_USE_POSIXSEM_SERIALIZE  
  151.     printf(" -D APR_USE_POSIXSEM_SERIALIZE\n");  
  152. #endif  
  153.   
  154. #if APR_USE_FCNTL_SERIALIZE  
  155.     printf(" -D APR_USE_FCNTL_SERIALIZE\n");  
  156. #endif  
  157.   
  158. #if APR_USE_PROC_PTHREAD_SERIALIZE  
  159.     printf(" -D APR_USE_PROC_PTHREAD_SERIALIZE\n");  
  160. #endif  
  161.   
  162. #if APR_USE_PTHREAD_SERIALIZE  
  163.     printf(" -D APR_USE_PTHREAD_SERIALIZE\n");  
  164. #endif  
  165.   
  166. #if APR_PROCESS_LOCK_IS_GLOBAL  
  167.     printf(" -D APR_PROCESS_LOCK_IS_GLOBAL\n");  
  168. #endif  
  169.   
  170. #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT  
  171.     printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n");  
  172. #endif  
  173.   
  174. #if APR_HAS_OTHER_CHILD  
  175.     printf(" -D APR_HAS_OTHER_CHILD\n");  
  176. #endif  
  177.   
  178. #ifdef AP_HAVE_RELIABLE_PIPED_LOGS  
  179.     printf(" -D AP_HAVE_RELIABLE_PIPED_LOGS\n");  
  180. #endif  
  181.   
  182. #ifdef BUFFERED_LOGS  
  183.     printf(" -D BUFFERED_LOGS\n");  
  184. #ifdef PIPE_BUF  
  185.     printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF);  
  186. #endif  
  187. #endif  
  188.   
  189.     printf(" -D DYNAMIC_MODULE_LIMIT=%ld\n",(long)DYNAMIC_MODULE_LIMIT);  
  190.   
  191. #if APR_CHARSET_EBCDIC  
  192.     printf(" -D APR_CHARSET_EBCDIC\n");  
  193. #endif  
  194.   
  195. #ifdef NEED_HASHBANG_EMUL  
  196.     printf(" -D NEED_HASHBANG_EMUL\n");  
  197. #endif  
  198.   
  199. #ifdef SHARED_CORE  
  200.     printf(" -D SHARED_CORE\n");  
  201. #endif  
  202.   
  203. /* This list displays the compiled in default paths: */  
  204. #ifdef HTTPD_ROOT  
  205.     printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n");  
  206. #endif  
  207.   
  208. #ifdef SUEXEC_BIN  
  209.     printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n");  
  210. #endif  
  211.   
  212. #if defined(SHARED_CORE) && defined(SHARED_CORE_DIR)  
  213.     printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n");  
  214. #endif  
  215.   
  216. #ifdef DEFAULT_PIDLOG  
  217.     printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n");  
  218. #endif  
  219.   
  220. #ifdef DEFAULT_SCOREBOARD  
  221.     printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n");  
  222. #endif  
  223.   
  224. #ifdef DEFAULT_LOCKFILE  
  225.     printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n");  
  226. #endif  
  227.   
  228. #ifdef DEFAULT_ERRORLOG  
  229.     printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n");  
  230. #endif  
  231.   
  232. #ifdef AP_TYPES_CONFIG_FILE  
  233.     printf(" -D AP_TYPES_CONFIG_FILE=\"" AP_TYPES_CONFIG_FILE "\"\n");  
  234. #endif  
  235.   
  236. #ifdef SERVER_CONFIG_FILE  
  237.     printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n");  
  238. #endif  
  239. }  
  240.   
  241. #define TASK_SWITCH_SLEEP 10000  
  242.   
  243. static void destroy_and_exit_process(process_rec *process,  
  244.                                      int process_exit_value)  
  245. {  
  246.     /* 
  247.      * Sleep for TASK_SWITCH_SLEEP micro seconds to cause a task switch on 
  248.      * OS layer and thus give possibly started piped loggers a chance to 
  249.      * process their input. Otherwise it is possible that they get killed 
  250.      * by us before they can do so. In this case maybe valueable log messages 
  251.      * might get lost. 
  252.      */  
  253.     apr_sleep(TASK_SWITCH_SLEEP);  
  254.     apr_pool_destroy(process->pool); /* and destroy all descendent pools */  
  255.     apr_terminate();  
  256.     exit(process_exit_value);  
  257. }  
  258.   
  259. static process_rec *init_process(int *argc, const char * const * *argv)  
  260. {  
  261.     process_rec *process;  
  262.     apr_pool_t *cntx;  
  263.     apr_status_t stat;  
  264.     const char *failed = "apr_app_initialize()";  
  265.   
  266.     stat = apr_app_initialize(argc, argv, NULL);  
  267.     if (stat == APR_SUCCESS) {  
  268.         failed = "apr_pool_create()";  
  269.         stat = apr_pool_create(&cntx, NULL);  
  270.     }  
  271.   
  272.     if (stat != APR_SUCCESS) {  
  273.         /* For all intents and purposes, this is impossibly unlikely, 
  274.          * but APR doesn't exist yet, we can't use it for reporting 
  275.          * these earliest two failures; 
  276.          */  
  277.         char ctimebuff[APR_CTIME_LEN];  
  278.         apr_ctime(ctimebuff, apr_time_now());  
  279.         fprintf(stderr, "[%s] [crit] (%d) %s: %s failed "  
  280.                         "to initial context, exiting\n",   
  281.                         ctimebuff, stat, (*argv)[0], failed);  
  282.         apr_terminate();  
  283.         exit(1);  
  284.     }  
  285.   
  286.     apr_pool_tag(cntx, "process");  
  287.     ap_open_stderr_log(cntx);  
  288.   
  289.     /* Now we have initialized apr and our logger, no more 
  290.      * exceptional error reporting required for the lifetime 
  291.      * of this server process. 
  292.      */  
  293.   
  294.     process = apr_palloc(cntx, sizeof(process_rec)); /* 分配内存,cntx为分配内存的地址 */  
  295.     process->pool = cntx;  
  296.   
  297.     apr_pool_create(&process->pconf, process->pool);  
  298.     apr_pool_tag(process->pconf, "pconf");  
  299.     process->argc = *argc;  
  300.     process->argv = *argv;  
  301.     process->short_name = apr_filepath_name_get((*argv)[0]);  
  302.     return process;  
  303. }  
  304.   
  305. static void usage(process_rec *process)  
  306. {  
  307.     const char *bin = process->argv[0];  
  308.     int pad_len = strlen(bin);  
  309.   
  310. #ifdef SHARED_CORE  
  311.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL ,  
  312.                  "Usage: %s [-R directory] [-D name] [-d directory] [-f file]",  
  313.                  bin);  
  314. #else  
  315.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  316.                  "Usage: %s [-D name] [-d directory] [-f file]", bin);  
  317. #endif  
  318.   
  319.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  320.                  "       %*s [-C \"directive\"] [-c \"directive\"]", pad_len, " ");  
  321.   
  322. #ifdef WIN32  
  323.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  324.                  "       %*s [-w] [-k start|restart|stop|shutdown]", pad_len, " ");  
  325.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  326.                  "       %*s [-k install|config|uninstall] [-n service_name]",  
  327.                  pad_len, " ");  
  328. #endif  
  329. #ifdef AP_MPM_WANT_SIGNAL_SERVER  
  330. #ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN  
  331.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  332.                  "       %*s [-k start|restart|graceful|graceful-stop|stop]",  
  333.                  pad_len, " ");  
  334. #else  
  335.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  336.                  "       %*s [-k start|restart|graceful|stop]", pad_len, " ");  
  337. #endif /* AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN */  
  338. #endif  
  339.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  340.                  "       %*s [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S]",  
  341.                  pad_len, " ");  
  342.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  343.                  "Options:");  
  344.   
  345. #ifdef SHARED_CORE  
  346.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  347.                  "  -R directory       : specify an alternate location for "  
  348.                  "shared object files");  
  349. #endif  
  350.   
  351.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  352.                  "  -D name            : define a name for use in "  
  353.                  "<IfDefine name> directives");  
  354.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  355.                  "  -d directory       : specify an alternate initial "  
  356.                  "ServerRoot");  
  357.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  358.                  "  -f file            : specify an alternate ServerConfigFile");  
  359.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  360.                  "  -C \"directive\"     : process directive before reading "  
  361.                  "config files");  
  362.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  363.                  "  -c \"directive\"     : process directive after reading "  
  364.                  "config files");  
  365.   
  366. #ifdef NETWARE  
  367.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  368.                  "  -n name            : set screen name");  
  369. #endif  
  370. #ifdef WIN32  
  371.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  372.                  "  -n name            : set service name and use its "  
  373.                  "ServerConfigFile");  
  374.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  375.                  "  -k start           : tell Apache to start");  
  376.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  377.                  "  -k restart         : tell running Apache to do a graceful "  
  378.                  "restart");  
  379.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  380.                  "  -k stop|shutdown   : tell running Apache to shutdown");  
  381.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  382.                  "  -k install         : install an Apache service");  
  383.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  384.                  "  -k config          : change startup Options of an Apache "  
  385.                  "service");  
  386.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  387.                  "  -k uninstall       : uninstall an Apache service");  
  388.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  389.                  "  -w                 : hold open the console window on error");  
  390. #endif  
  391.   
  392.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  393.                  "  -e level           : show startup errors of level "  
  394.                  "(see LogLevel)");  
  395.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  396.                  "  -E file            : log startup errors to file");  
  397.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  398.                  "  -v                 : show version number");  
  399.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  400.                  "  -V                 : show compile settings");  
  401.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  402.                  "  -h                 : list available command line options "  
  403.                  "(this page)");  
  404.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  405.                  "  -l                 : list compiled in modules");  
  406.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  407.                  "  -L                 : list available configuration "  
  408.                  "directives");  
  409.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  410.                  "  -t -D DUMP_VHOSTS  : show parsed settings (currently only "  
  411.                  "vhost settings)");  
  412.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  413.                  "  -S                 : a synonym for -t -D DUMP_VHOSTS");  
  414.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  415.                  "  -t -D DUMP_MODULES : show all loaded modules ");  
  416.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  417.                  "  -M                 : a synonym for -t -D DUMP_MODULES");  
  418.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  419.                  "  -t                 : run syntax check for config files");  
  420.     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,  
  421.                 "  -T                 : start without DocumentRoot(s) check");  
  422.   
  423.     destroy_and_exit_process(process, 1);  
  424. }  
  425.   
  426. int main(int argc, const char * const argv[])  
  427. {  
  428.     char c;  
  429.     int configtestonly = 0;  
  430.     const char *confname = SERVER_CONFIG_FILE;  
  431.     const char *def_server_root = HTTPD_ROOT;  
  432.     const char *temp_error_log = NULL;  
  433.     const char *error;  
  434.     process_rec *process;  
  435.     server_rec *server_conf;  
  436.     apr_pool_t *pglobal; /* 基本池类型 */  
  437.     apr_pool_t *pconf;  
  438.     apr_pool_t *plog; /* Pool of log streams, reset _after_ each read of conf */  
  439.     apr_pool_t *ptemp; /* Pool for temporary config stuff, reset often */  
  440.     apr_pool_t *pcommands; /* Pool for -D, -C and -c switches */  
  441.     apr_getopt_t *opt;  
  442.     apr_status_t rv;  
  443.     module **mod;  
  444.     const char *optarg;  
  445.     APR_OPTIONAL_FN_TYPE(ap_signal_server) *signal_server;  
  446.   
  447.     AP_MONCONTROL(0); /* turn off profiling of startup 代码分析开关 */  
  448.   
  449.     process = init_process(&argc, &argv); /* process_rec表示进程的结构体 */  
  450.     pglobal = process->pool;  
  451.     pconf = process->pconf;  
  452.     ap_server_argv0 = process->short_name;  
  453.   
  454. #if APR_CHARSET_EBCDIC  
  455.     if (ap_init_ebcdic(pglobal) != APR_SUCCESS) {  
  456.         destroy_and_exit_process(process, 1);  
  457.     }  
  458. #endif  
  459.   
  460.     apr_pool_create(&pcommands, pglobal); /* 创建命令行参数池 */  
  461.     apr_pool_tag(pcommands, "pcommands"); /* 给参数池加一个标签 */  
  462.     ap_server_pre_read_config  = apr_array_make(pcommands, 1, sizeof(char *));  
  463.     ap_server_post_read_config = apr_array_make(pcommands, 1, sizeof(char *));  
  464.     ap_server_config_defines   = apr_array_make(pcommands, 1, sizeof(char *));  
  465.   
  466.     error = ap_setup_prelinked_modules(process);  
  467.     if (error) {  
  468.         ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, 0, NULL, "%s: %s",  
  469.                      ap_server_argv0, error);  
  470.         destroy_and_exit_process(process, 1);  
  471.     }  
  472.   
  473.     ap_run_rewrite_args(process);  
  474.   
  475.     /* Maintain AP_SERVER_BASEARGS list in http_main.h to allow the MPM 
  476.      * to safely pass on our args from its rewrite_args() handler. 
  477.      */  
  478.     apr_getopt_init(&opt, pcommands, process->argc, process->argv);  
  479.   
  480.     while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg))  
  481.             == APR_SUCCESS) {  
  482.         char **new;  
  483.   
  484.         switch (c) {  
  485.         case 'c':  
  486.             new = (char **)apr_array_push(ap_server_post_read_config);  
  487.             *new = apr_pstrdup(pcommands, optarg);  
  488.             break;  
  489.   
  490.         case 'C':  
  491.             new = (char **)apr_array_push(ap_server_pre_read_config);  
  492.             *new = apr_pstrdup(pcommands, optarg);  
  493.             break;  
  494.   
  495.         case 'd':  
  496.             def_server_root = optarg;  
  497.             break;  
  498.   
  499.         case 'D':  
  500.             new = (char **)apr_array_push(ap_server_config_defines);  
  501.             *new = apr_pstrdup(pcommands, optarg);  
  502.             /* Setting -D DUMP_VHOSTS is equivalent to setting -S */  
  503.             if (strcmp(optarg, "DUMP_VHOSTS") == 0)  
  504.                 configtestonly = 1;  
  505.             /* Setting -D DUMP_MODULES is equivalent to setting -M */  
  506.             if (strcmp(optarg, "DUMP_MODULES") == 0)  
  507.                 configtestonly = 1;  
  508.             break;  
  509.   
  510.         case 'e':  
  511.             if (strcasecmp(optarg, "emerg") == 0) {  
  512.                 ap_default_loglevel = APLOG_EMERG;  
  513.             }  
  514.             else if (strcasecmp(optarg, "alert") == 0) {  
  515.                 ap_default_loglevel = APLOG_ALERT;  
  516.             }  
  517.             else if (strcasecmp(optarg, "crit") == 0) {  
  518.                 ap_default_loglevel = APLOG_CRIT;  
  519.             }  
  520.             else if (strncasecmp(optarg, "err", 3) == 0) {  
  521.                 ap_default_loglevel = APLOG_ERR;  
  522.             }  
  523.             else if (strncasecmp(optarg, "warn", 4) == 0) {  
  524.                 ap_default_loglevel = APLOG_WARNING;  
  525.             }  
  526.             else if (strcasecmp(optarg, "notice") == 0) {  
  527.                 ap_default_loglevel = APLOG_NOTICE;  
  528.             }  
  529.             else if (strcasecmp(optarg, "info") == 0) {  
  530.                 ap_default_loglevel = APLOG_INFO;  
  531.             }  
  532.             else if (strcasecmp(optarg, "debug") == 0) {  
  533.                 ap_default_loglevel = APLOG_DEBUG;  
  534.             }  
  535.             else {  
  536.                 usage(process);  
  537.             }  
  538.             break;  
  539.   
  540.         case 'E':  
  541.             temp_error_log = apr_pstrdup(process->pool, optarg);  
  542.             break;  
  543.   
  544.         case 'X':  
  545.             new = (char **)apr_array_push(ap_server_config_defines);  
  546.             *new = "DEBUG";  
  547.             break;  
  548.   
  549.         case 'f':  
  550.             confname = optarg;  
  551.             break;  
  552.   
  553.         case 'v':  
  554.             printf("Server version: %s\n", ap_get_server_description());  
  555.             printf("Server built:   %s\n", ap_get_server_built());  
  556.             destroy_and_exit_process(process, 0);  
  557.   
  558.         case 'V':  
  559.             show_compile_settings();  
  560.             destroy_and_exit_process(process, 0);  
  561.   
  562.         case 'l':  
  563.             ap_show_modules();  
  564.             destroy_and_exit_process(process, 0);  
  565.   
  566.         case 'L':  
  567.             ap_show_directives();  
  568.             destroy_and_exit_process(process, 0);  
  569.   
  570.         case 't':  
  571.             configtestonly = 1;  
  572.             break;  
  573.   
  574.        case 'T':  
  575.            ap_document_root_check = 0;  
  576.            break;  
  577.   
  578.         case 'S':  
  579.             configtestonly = 1;  
  580.             new = (char **)apr_array_push(ap_server_config_defines);  
  581.             *new = "DUMP_VHOSTS";  
  582.             break;  
  583.   
  584.         case 'M':  
  585.             configtestonly = 1;  
  586.             new = (char **)apr_array_push(ap_server_config_defines);  
  587.             *new = "DUMP_MODULES";  
  588.             break;  
  589.   
  590.         case 'h':  
  591.         case '?':  
  592.             usage(process);  
  593.         }  
  594.     }  
  595.   
  596.     /* bad cmdline option?  then we die */  
  597.     if (rv != APR_EOF || opt->ind < opt->argc) {  
  598.         usage(process);  
  599.     }  
  600.   
  601.     apr_pool_create(&plog, pglobal);  
  602.     apr_pool_tag(plog, "plog");  
  603.     apr_pool_create(&ptemp, pconf);  
  604.     apr_pool_tag(ptemp, "ptemp");  
  605.   
  606.     /* Note that we preflight the config file once 
  607.      * before reading it _again_ in the main loop. 
  608.      * This allows things, log files configuration 
  609.      * for example, to settle down. 
  610.      */  
  611.   
  612.     ap_server_root = def_server_root;  
  613.     if (temp_error_log) {  
  614.         ap_replace_stderr_log(process->pool, temp_error_log);  
  615.     }  
  616.     server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);  
  617.     if (!server_conf) {  
  618.         destroy_and_exit_process(process, 1);  
  619.     }  
  620.   
  621.     if (ap_run_pre_config(pconf, plog, ptemp) != OK) {  
  622.         ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,  
  623.                      NULL, "Pre-configuration failed");  
  624.         destroy_and_exit_process(process, 1);  
  625.     }  
  626.   
  627.     rv = ap_process_config_tree(server_conf, ap_conftree,  
  628.                                 process->pconf, ptemp);  
  629.     if (rv == OK) {  
  630.         ap_fixup_virtual_hosts(pconf, server_conf);  
  631.         ap_fini_vhost_config(pconf, server_conf);  
  632.         apr_hook_sort_all();  
  633.   
  634.         if (configtestonly) {  
  635.             ap_run_test_config(pconf, server_conf);  
  636.             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Syntax OK");  
  637.             destroy_and_exit_process(process, 0);  
  638.         }  
  639.     }  
  640.   
  641.     signal_server = APR_RETRIEVE_OPTIONAL_FN(ap_signal_server);  
  642.     if (signal_server) {  
  643.         int exit_status;  
  644.   
  645.         if (signal_server(&exit_status, pconf) != 0) {  
  646.             destroy_and_exit_process(process, exit_status);  
  647.         }  
  648.     }  
  649.   
  650.     /* If our config failed, deal with that here. */  
  651.     if (rv != OK) {  
  652.         destroy_and_exit_process(process, 1);  
  653.     }  
  654.   
  655.     apr_pool_clear(plog);  
  656.   
  657.     if ( ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {  
  658.         ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
  659.                      0, NULL, "Unable to open logs");  
  660.         destroy_and_exit_process(process, 1);  
  661.     }  
  662.   
  663.     if ( ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {  
  664.         ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,  
  665.                      NULL, "Configuration Failed");  
  666.         destroy_and_exit_process(process, 1);  
  667.     }  
  668.   
  669.     apr_pool_destroy(ptemp);  
  670.   
  671.     for (;;) {  
  672.         apr_hook_deregister_all();  
  673.         apr_pool_clear(pconf);  
  674.   
  675.         for (mod = ap_prelinked_modules; *mod != NULL; mod++) {  
  676.             ap_register_hooks(*mod, pconf);  
  677.         }  
  678.   
  679.         /* This is a hack until we finish the code so that it only reads 
  680.          * the config file once and just operates on the tree already in 
  681.          * memory.  rbb 
  682.          */  
  683.         ap_conftree = NULL;  
  684.         apr_pool_create(&ptemp, pconf);  
  685.         apr_pool_tag(ptemp, "ptemp");  
  686.         ap_server_root = def_server_root;  
  687.         server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);  
  688.         if (!server_conf) {  
  689.             destroy_and_exit_process(process, 1);  
  690.         }  
  691.   
  692.         if (ap_run_pre_config(pconf, plog, ptemp) != OK) {  
  693.             ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
  694.                          0, NULL, "Pre-configuration failed");  
  695.             destroy_and_exit_process(process, 1);  
  696.         }  
  697.   
  698.         if (ap_process_config_tree(server_conf, ap_conftree, process->pconf,  
  699.                                    ptemp) != OK) {  
  700.             destroy_and_exit_process(process, 1);  
  701.         }  
  702.         ap_fixup_virtual_hosts(pconf, server_conf);  
  703.         ap_fini_vhost_config(pconf, server_conf);  
  704.         apr_hook_sort_all();  
  705.         apr_pool_clear(plog);  
  706.         if (ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {  
  707.             ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
  708.                          0, NULL, "Unable to open logs");  
  709.             destroy_and_exit_process(process, 1);  
  710.         }  
  711.   
  712.         if (ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {  
  713.             ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,  
  714.                          0, NULL, "Configuration Failed");  
  715.             destroy_and_exit_process(process, 1);  
  716.         }  
  717.   
  718.         apr_pool_destroy(ptemp);  
  719.         apr_pool_lock(pconf, 1);  
  720.   
  721.         ap_run_optional_fn_retrieve();  
  722.   
  723.         if (ap_mpm_run(pconf, plog, server_conf))  
  724.             break;  
  725.   
  726.         apr_pool_lock(pconf, 0);  
  727.     }  
  728.   
  729.     apr_pool_lock(pconf, 0);  
  730.     destroy_and_exit_process(process, 0);  
  731.   
  732.     return 0; /* Termination 'ok' */  
  733. }  
  734.   
  735. #ifdef AP_USING_AUTOCONF  
  736. /* This ugly little hack pulls any function referenced in exports.c into 
  737.  * the web server.  exports.c is generated during the build, and it 
  738.  * has all of the APR functions specified by the apr/apr.exports and 
  739.  * apr-util/aprutil.exports files. 
  740.  */  
  741. const void *suck_in_APR(void);  
  742. const void *suck_in_APR(void)  
  743. {  
  744.     extern const void *ap_ugly_hack;  
  745.   
  746.     return ap_ugly_hack;  
  747. }  
  748. #endif  
Apache HTTP连接处理流程

总结帖:

 

 

首先更新虚拟主机信息,利用process_connection挂钩函数处理。

 

1. 请求读取 ap_read_request

报文解析,HTTP_PROTOCOL

 

HTTP请求头:"GET index.html HTTP/1.1"

HTTP请求域:"Accept:*/*"

HTTP请求体:数据

 

请求信息保存在request_rec中,在请求处理过程中一直存在。

 

读取报文的处理也在这个地方做,输入处理过滤器。比如说文件分隔符(Windows "/", Unix "\")。

 

2. 请求处理 ap_process_request
(1):请求解析

URL字符转义

浏览器会对地址栏输入的特殊字符进行转化,例如空格转换成%20,因此服务器需要将字符转换回去。处理函数是:ap_unescape_url()。

 

从URL中剔除/../和/./字符

确保路径成为绝对路径。处理函数是ap_getparents()。

 

首次读取URL相关配置信息

从配置系统中查找与该URL相关联的配置信息,为后续的用户权限验证,权限控制做准备。处理函数是ap_location_walk()。

 

URL名称转换 translate_name

指令Alias:将URL映射到另外一个特定的URL。mod_writer -- URL重写。

 

map to storage

确定指定的资源在磁盘上是否存在。

 

二次读取URL相关配置信息

URL名称转换和映射后,读取改新URL的相关联的配置信息。

 

head parser

检查HTTP请求头。

 

(2):安全处理

access_checker

对客户的访问做基础性的检查限制工作。比如根据IP地址,访问时间。模块是mod_access。

 

check_user_id

检查用户的身份权限(用户名和密码)。

 

auth_checker

检查用户是否具有访问该资源的权限。

(3):请求准备

type_checker

确认客户请求的资源类型。mod_mime。

 

fixups

补丁修复。可选处理过程。在内容生成以前的最后一步处理操作。

 

 

Apache Architecture:

How do we measure performance? 如何测量web服务器的性能?

 

–Requests per Second 每秒请求次数 

–Bandwidth 带宽 

–Latency 等待时间 

–Concurrency (Scalability) 并发(可扩展)

Building a scalable web server:设计可扩展的web server

 

handling an HTTP request 处理http请求 

–map the URL to a resource 将url映射成资源 

–check whether client has permission to access the resource 检查客户端是否有访问资源的权限  –choose a handler and generate a response 选择处理器和生成响应 

–transmit the response to the client 发送响应到客户端 

–log the request 记录请求日志

must handle many clients simultaneously 必须同时处理多个请求

must do this as fast as possible 必须尽可能快的处理

Resource Pools:资源池

 

one bottleneck to server performance is the operating system 服务器性能瓶颈之一是操作系统 

–system calls to allocate memory, access a file, or create a child process take significant amounts of time

分配内存,访问文件和创建子进程的系统调用会消耗大量时间

–as with many scaling problems in computer systems, caching is one solution

计算机系统中的许多扩展性的问题,缓存是一种解决方法

resource pool: application-level data structure to allocate and cache resources 应用层数据存储的分配和缓存资源

–allocate and free memory in the application instead of using a system call 

在应用中分配和释放内存,用来代替使用系统调用

–cache files, URL mappings, recent responses 

缓存文件,url映射,最近产生的回复(响应)

–limits critical functions to a small, well-tested part of code 

把至关重要的函数限制到小的,充分测试的代码

 

Multi-Processor Architectures:多处理器体系结构

a critical factor in web server performance is how each new connection is handled

web服务器性能至关重要的因素之一就是每一个连接如何被处理

–common optimization strategy: identify the most commonly-executed code and make this run as fast as possible

通常的优化策略:识别最频繁执行的代码并且让它执行的尽可能快

–common case: accept a client and return several static objects

通常的情况:接受客户端请求,返回几个静态对象

–make this run fast: pre-allocate a process or thread, cache commonly-used files and the HTTP message for the response

让这运行的更快:预分配线程或进程,缓存经常使用的文件和http消息

 

Connections:连接

must multiplex handling many connections simultaneously 必须同时处理多个连接

–select(), poll(): event-driven, singly-threaded 事件驱动,单线程的

–fork(): create a new process for a connection 为每个连接创建一个进程 

–pthread create(): create a new thread for a connection 为每个连接创建一个线程

synchronization among processes/threads 进程/线程间同步

–shared memory: semaphores message passing 共享内存:信号消息传递

 

Select:

select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

Allows a process to block until data is available on any one of a set of file descriptors.

允许进程阻断,直到一系列文件描述符中的一个准备好

One web server process can service hundards of socket connections

一个web服务器进程可以服务于几百个连接

 

Event Driven Architecture:事件驱动体系结构

one process handles all events 一个进程处理多个事件

must multiplex handling of many clients and their messages 

必须多路处理多个客户端和它们的消息

use select() or poll() to multiplex socket I/O events 使用select或poll来多路复用io事件

provide a list of sockets waiting for I/O events 提供一个socket列表来等待io事件

sleeps until an event occurs on one or more sockets 休眠直到多个socket的事件发生

can provide a timeout to limit waiting time 可以提供一个超时时间来限制等待时间

must use non-blocking system calls 必须使用非阻塞系统调用

some evidence that it can be more efficient than process or thread architectures 一些证据表明比进线程更有效率

 

Process Driven Architecture:进程驱动体系结构

devote a separate process/thread to each event 每个线程或进程专注于一个事件

master process listens for connections 主进程监听连接

master creates a separate process/thread for each new connection 主进程为每一个连接创建独立的进程/线程

performance considerations 性能考虑

creating a new process involves significant overhead 创建一个进程需要大量的系统开销

threads are less expensive, but still involve overhead 线程相比少了很多,但仍然需要系统开销

may create too many processes/threads on a busy server 可能在一个繁忙的服务器上创建太多的进程/线程

 

Process/Thread Pool Architecture:进程/线程池体系结构:

master thread 主线程

creates a pool of threads 创建线程池

listens for incoming connections 监听到来的连接

places connections on a shared queue 把连接放到共享队列

processes/threads 进程/线程

take connections from shared queue 从共享队列里获取线程

handle one I/O event for the connection 为每个连接处理io事件

return connection to the queue 返回连接给队列

live for a certain number of events (prevents long-lived memory leaks) 有一定数量的事件(阻止长寿命的内存泄露)

need memory synchronization 需要内存同步

 

Hybrid Architectures:复杂体系结构:

each process can handle multiple requests 每一个进程处理多个请求

each process is an event-driven server 每一个进程是一个事件驱动服务器

must coordinate switching among events/requests 必须在事件和请求间切换

each process controls several threads 每一个进程控制几个线程

threads can share resources easily 线程间可以容易的共享资源

requires some synchronization primitives 需要一些同步原语

event driven server that handles fast tasks but spawns helper processes for time-consuming requests

事件驱动服务器处理快速任务,而生成助手进程来处理耗时的请求

 

What makes a good Web Server?:什么东西可以设计一个优秀的web服务器

Correctness 准确性

Reliability 可靠性

Scalability 可扩展性

Stability 稳定性

Speed 速度

 

Correctness 准确性

Does it conform to the HTTP specification? 符合http协议标准吗?

Does it work with every browser? 对每一个浏览器起作用吗?

Does it handle erroneous input gracefully? 能优雅的处理错误的输入吗?

 

Reliability 可靠性

Can you sleep at night? 你晚上可以休息吗?

Are you being paged during dinner? 吃晚饭的时候被打断了吗?

It is an appliance? 这是一个设备,工具?

 

 

Scalability 可扩展性

Does it handle nominal load? 它可以处理标称的负载吗?

Have you been Slashdotted? 

And did you survive?

What is your peak load? 峰值负载时多少?

 

Speed 速度

Does it feel fast? 速度快吗?

Do pages snap in quickly? 

Do users often reload pages?

 

接触openssl已经有一段时间了,我读过很多源码,感觉不错的也就那么几个,linux内核是其中之 一,openssl也是其中之一。openssl说白了不是什么功能性的东西,而是提供了一个支撑性的底层框架,本质上和linux内核一样,但是和 apache有区别,apache明确的实现了一个功能,就是web服务器,而openssl中我认为最重要的就是它提供的BIO框架和EVP框架,与其 说openssl是一个ssl的实现不如说ssl只不过是openssl框架的一个demo,本质上openssl提供了一套抽象的IO接口,这就是 BIO,还有一套很容易使用的加密解密接口,这就是EVP,最后证实ssl这个协议使用了上述两类接口,如果从这个意义上讲的话,apache其实也是这 样的,在构建顶层的web服务器之前首先实现了一套apr通用框架,甚至memcahce使用的libevent也是这样,这就是说很多的成功的开源的代 码与其说是一种专用的程序倒不如说包含有一套通用的框架,而这个带代码的原始意义最后退化成了使用这个通用框架的一个实例,最起码我看好的几个开源项目都 是这样。策略和机制分离,可扩展性十分棒,用起来十分灵活。 
     openssl总的来说由三部分组成,一个是BIO,一个是EVP,还有一个就是构建于二者之上的ssl实现。研究这套代码实现给我们可以带来很多的启 示,其中最重要的我想就是IO的意义,还是先拿linux内核来说事吧,其实多任务操作系统提供给用户n台冯诺依曼机器,不但将逻辑抽象给了用户,而且将 大部分cpu指令也抽象给了用户,唯独不包括IO指令,因为外设不是多任务的,外设无法自己处理好多个任务的互斥关系,所有的多个任务共享一个外设,因此 操作系统内核必须进行管理,这就是操作系统内核提供的IO系统调用的作用,也就是说IO指令必须通过系统调用进入操作系统内核之后让内核帮忙处理。以上就 是io的意义,对于openssl提供BIO接口其实意义和上面的差不多,不同的是在原始的io指令发送给操作系统之前提供了用户处理的机会,也就是说用 户可以过滤发送给内核的io系统调用的消息,比如加密,分段等等。如果按照网络的观点,协议栈提供一幅和io截然不同的图景,虽然协议栈最终也要将数据给 了网卡和电缆,但是网卡作为一个外设的意义被更抽象的物理层和链路层取代了,人们不再认为发送网络数据就是共享网卡外设或者电缆,而更多讨论以太网 csma/cd模型,或者ppp链路规程之类的抽象的概念,因此对于网络数据的io,协议栈将一切都抽象了,一切都抽象成了分层的模型,这就是分层模型的 重大意义,人们可以在共享的网卡和电缆上发送不共享的数据,一切井井有条。不像普通的打印机数据发送,网络数据的发送方式截然不同,如果你想发送数据给打 印机,那么首先你必须首先得到打印机的文件描述符,如果另一个用户也要发送数据给打印机,那么操作系统内核的文件系统模块此时将协调它们的顺序,如果网络 数据的话,协议栈就搞定了一切,在用户层不需要类似的文件描述符的概念,而相同意义的概念就是套接字,需要一个ip地址和一个端口号,这就是协议栈的功 能,然后协议栈会将数据一直传输到最底层的物理层,这种方式看起来要比文件的方式更加直观,因此unix/linux中没有将网卡抽象成设备文件,毕竟协 议栈完成了相同的功能。当然套接字接口还是和文件接口相统一的,但是这仅仅是为了操作的统一和方便,普通的设备将通过打开一个设备文件的方式得到文件描述 符,而套接字的方式完全按照协议栈的要求,需要提供一个ip地址和一个端口号以及协议族。在openssl中,你既可以通过普通io的方式也就是说设备文 件的方式扩展传统io模型,也可以使用网络协议栈的方式扩展io模型,实际上openssl框架将二者统一成为一体。 
首先,如果使用普通的设备文件方式的io,那么我们只需要BIO就可以了,因为BIO是链式的,所以你可以随意进行io过滤,如果想扩展网络协议栈,鲁迅 式的废话就是同样只需要BIO就可以了,但是条件是最下面的BIO必须是一个套接字,因为我们要在应用层扩展并且只能在应用层扩展,那么就必然要在套接字 之上扩展,ssl就是在普通套接字之上加入了安全保护功能。BIO统一了两种方式,其实目前为止也就这两种io的方式,一种是以可以抽象为设备文件的设备 为载体的IO,这种IO又可细分为块设备和字符设备的IO,对于块设备往往通过文件系统来访问;另一种以网络协议栈方式进行的网络IO,协议栈实现几乎一 切流程,整个协议栈的分层模型和文件系统/设备驱动的分层模型类似,openssl的BIO可以包容这二者的任何一种,并且还可以混合它们,本质上BIO 可以组合成通向世界任何一个地方的管道。 
对于EVP,它提供了很多易用的加密解密接口函数,openssl本身已经自带了很多加密解密算法,利用这套EVP接口,你甚至可以轻松实现自己的加密解 密算法。EVP接口中封装了算法的流程,具体的算法由不同的策略来填充,这一点上所有优秀的东西几乎是一致的。 
     openssl导出的接口是十分容易使用的,基本都是一看声明就知道其用处。以下这个例子读取并且解析一个证书: 
X509 * cert = NULL; 
BIO *pbio = BIO_new_file((const char*)strPath, "r"); 
cert = PEM_read_bio_X509(pbio,NULL,NULL,NULL);

tomcat是一个很优秀的轻量级的web引擎和java容器,它本身用java开发,而java是一个面向对象的语言,因此面向对象的语言开发出来的tomcat当然也就继承了面向对象的特征,tomcat还说明,OO真的使得开发变得简单,而且开发出来的产品天生就带着OO的特点,不光开发和维护本身,就连产品也OO了。反过来看apache,标准c语言开发,纯粹过程式的处理,十分符合http的处理流程,单个http请求本身的处理就是串行的过程化的,其实那个年代的东西都是这样,比如c语言,比如unix,当然还包括Tcp/ip协议族,虽然c开发的apache很稳定,性能也不错,但是它的最大的用武之地在于它对静态页面的推送,这也是http协议最基本的初衷,随着web应用日趋复杂,静态页面已经不能解决所有的问题,于是大量的动态页面出来了,像asp,php,jsp等,此时,apache本身解析并推送处理这些动态页面就有些力不从心了,毕竟这不是它的强项,即使设计了相应的模块,那么由于apache设计上的因素,对于动态页面它还是不适合的,不过由于apache性能以及稳定性的优势,它很适合做web服务器的前端,将请求定向给后端复杂的应用服务器或者动态页面容器,然后从后端接收处理后的静态元素,最终推送给客户端web浏览器。从过滤器这个web服务器的一个模块最能看出该web服务器的架构。 
     apache以往的过滤器都是连成一条链,分为输入过滤链和输出过滤链,前者可以将请求过滤而后者可以将推送给客户的内容过滤。所有的请求无一遗漏的要通过事先安好的所有的过滤钩子,这种方式虽然工作得很不错,但是由于页面都是静态的,所以缺点当然无法暴露,试想动态页面的处理结果,有些钩子只在特定的动态解析结果下起作用,对于大多数不起作用,如果将这个过滤钩子安装的话,很多处理都要在它里面白跑一趟,这很损失性能,或者说这很丑陋。apache新版本的做法是,实现了一个过滤器的动态安装,对于输出过滤器,在内容处理器处理完以后,也就是动态元素已经解析完毕,这个时候就可以根据解析的结果裁定要不要安装输出过滤器了,如果要的话,那么此时才安装,如果不要,就不安装了,而且,新的apache还可以为一个过滤器注册多个策略,在内容处理器的任务完毕之后根据结果裁决最终哪个策略作为过滤器被使用,如此一来就可以在某种意义上实现“每请求”的钩子过滤了,而不是以前的对所有的请求安装相同的过滤钩子,然后在钩子函数内部裁定对这个请求是否起作用。这可以说是apache从静态向动态发展的一项创举,但是apache天生的设计问题决定了它也只能走到这里,下面的主角该是tomcat了,它就是由于要解析动态jsp要求而生的。 
既然如此,tomcat的优势就不言自明了,它具有OO的特性,里面的模块设计得特别人性化,特别的OO,它虽然比不上apache的强大与灵活,但是它的设计我感觉比apache的要好得多,毕竟儿子终归要超越父亲的。在tomcat中实现了“每网站”配置,就是每个网站在遵循tomcat总体的大的配置之后还可以拥有自己个性的配置,总体的配置在tomcat安装目录\conf目录下,而每网站的配置在网站\WEB_INF目录下的web.xml文件,之所以如此就是因为tomcat是以动态jsp页面解析为主的,既然是动态的,那么当然需要很多的配置策略才可以进行,最起码你要告诉解析引擎在哪里可以找到一些servlet类,而每个网站使用的类很大程度上是不同的,因此每网站配置就成了必须,如此的解决方案就是,将所有网站的共性配置放到tomcat安装目录\conf\web.xml中,将个性配置放到每网站的配置中。对于过滤,tomcat也使用了不同于apache的方式,在tomcat中,解析引擎根据每网站的配置文件为每一个网站的每一个过滤器生成了一个唯一的实例,之后针对这个网站的所有的请求都使用这一个实例,如果你想设计一个过滤器针对于所有的网站,那么很好办,只需要将过滤器的代码放到所有网站共享的shared\classes目录下即可,然后再在公共的配置文件里配置过滤器,这样的好处在于可以方便的进行不同请求间的通信和同一请求的不同过滤器之间的通信,执行引擎只需要简单得将这些通信转化为类的对象之间的通信,而类的对象之间的通信在java的OO规范中有明确的定义。tomcat之所以可以如此灵活,全在于它的OO特性,在传统的apache中,一切都是以函数调用为基础的,因此很难在执行过程的层面上形象地表达真实世界,而tomcat的执行是以对象为基础的,对象的设计如果设计的好的话必然是现实世界的写照,因此执行过程才显得如此简单而易懂。 
因此可以说,apache并没有过时,它是针对http协议的,而tomcat是针对应用的,它和http协议没有冲突,如果你用tomcat作web服务器亲自处理http,那么我感觉不如用apache,如果你将tomcat作为一个serlet容器,这才是它的职责,apache和tomcat的分工是不同,apache使http协议实现的一个例子,而tomcat某种意义上是http上面的东西,为了使http更灵活,使http承载的内容更丰富,http为繁多的应用铺就了一条道路而已。简单的说,apache就是接受http请求,热后解析,最后将结果推送给客户端浏览器,这里的解析就是一个机制,具体怎么解析,http不管,而apache很多时候也是交给了可加载模块,让模块找到合适的解析者,比如tomcat,比如websphere,比如weblogic等等,这些已经不是apache本身的事情了,如果它们能如此分工的话,运行于它们之上的web网站一定很强大。

linux内核差一点就走进了windows的为需求而升级的发展道路从而偏离了它原来 的“只提供机制不提供策略”的道路,那就是在2.4内核时期,内核提供了一个叫做khttpd的内核级别的web服务器,当时linux的性能还不是很 好,比如进程调度算法还是O(n)的方式,而且不能有效利用多处理器的优势,线程的实现还是很拙劣,很多方面没有达到posix的高性能指标,因此当时的 开发者可能就钻进了解决性能瓶颈的牛角尖上,鉴于apache服务器很多都运行在linux上,而且web服务是如此的普遍和重要,因此性能瓶颈也就主要 是apache的性能瓶颈,因此面对这个如此“特化”而又如此不能不管的需求,开发者们只好就事论事地将web服务器单独加速,也就是在内核中实现一个 web加速服务,如果按照这条路走下去,linux最终还会在内核实现ftp,甚至任何用户的需求性的策略,还会把图形处理搬到内核,像windows NT做的那样,不过linux到了2.6内核以后重新找到了自己的坐标,因为没有必要再用那种特化需求的方式解决局部的性能瓶颈了,2.6内核的很多机制 都将性能提高到一个新的水平,以web服务器为例,经测试证明2.6下的web服务器完全比内核实现的khttpd加速服务拥有更好的性能,这证明 linux只提供机制是很好的,用户在这种机制上实现的策略性能并不差,这样责任便分来了,如果说用户的程序在用户使了大劲后性能还是不够好,那么就要看 看内核是否可以提供更好的机制。2.6内核的所作所为完全阻止了开发者们再在某一个方面进行优化,2.6内核的进步是整体的,比如新的调度算法,比如 2.6.17中的splice和tee系统调用,这些都使khttpd没有了存在下去的必要。策略怎么会污染机制呢?想象一下建筑,早先的是混砖式的结构,现在的大厦都成了全框架结构,看看有何不同,住混砖结构房屋的人可能都为装修犯过愁,因为为 了使客厅显得更大,那么就必须打掉一堵墙,可是那堵墙偏偏是承重墙,于是...现在的框架结构就没有这个问题,屋子里除了几个大柱子就没有别的什么了,你 想垒墙就垒墙,想打掉就打掉,这就是机制和策略,框架就是机制,而内部的墙就是策略,如果你把墙垒到了框架里面,势必会影响美观,也会带来不利影响,你将 再次承受承重墙的压力。在操作系统中,尽量不要去通过内核完成一些需求,内核的作用就是为全体进程服务而不是满足用户的个别需求,尽量在用户空间实现需 求,因为这才是需求的实现地,你在内核实现一个你自己进程的需求,如果别的进程不需要这个需求,那人家还是要承受你这个承重墙的压力,只有一个影响全系统 的需求才可以在内核实现,比如杀毒程序,比如防火墙。而khttpd仅仅是一个web加速服务,根本没有资格进入内核独占一地。 在一篇叫做《内核比较:2.4和2.6上的Web服务》的文章中列举了2.6内核的几个新特性:1.超线程支持,多处理支持无论何时都是不错 的;2.NUMA支持;3.线程改进,在内核中增强了posix的线程支持,比如使用NPTL,撤销了管理线程;4.O(1)调度程序,这个就不多说 了;5.I/O的改进,主要是“2.4 中的全局 I/O 请求锁不再使用。在 2.6 中块 I/O 缓冲区(kiobuf)允许 I/O 请求可以比 PAGE_SIZE 大。”;6.异步I/O,这个也不说了;7.这个是比上述6点更高一层次的,就是splice和tee的加入,这俩系统调用可以在用户空间操作内核,其实 就是一个一个内核缓冲的handle。以上7点使khttpd黯然退却,没有必要继续生存。 无论如何,我还是比较欣赏khttpd本身的设计的,很简单,它的代码框架就是:1.启动一些子内核线程在监听80端口(要看内核的khttpd是主 web服务器还是辅web服务器)的套接字上进行accept,并且实时跟踪子内核线程的数量,根据策略进行数量保持,也就是少了创建新的,多了就干 掉;2.子内核线程accept客户浏览器的连接,对于每一个连接进行处理;3.如果内核可以处理,那么直接将结果回写给客户端套接字;4.如果内核处理 不了,那么就呼叫用户端的套接字进行处理,其实就是交给了用户的web服务器。这些流程间涉及到一些技巧,还是看看代码吧: 

 

C代码 
  1. void NeedSpawn(struct socket *sock)   
  2. {       //InitCount表示已经创建的但是还没有接受连接的子线程;AcceptCount表示正在accept过程中的子线程   
  3.     while (atomic_read(&InitCount)+atomic_read(&AcceptCount)    {   
  4.         kernel_thread(khttpd_child,sock,0);   
  5.         atomic_inc(&InitCount);      
  6.     }   
  7. }   
  8. int khttpd_child(void *TMP)   
  9. {   
  10.     struct socket *sock,*newsock;   
  11.     struct sock *sk;   
  12.     int error;   
  13.     struct proto_ops *ops;   
  14.     char *Buffer;   
  15.     size_t BufLen;   
  16.     struct http_time  *httptime;   
  17.     current->session = 1;   
  18.     current->pgrp    = 1;   
  19.     current->state    |= TASK_WAKE_ONE;   
  20.     sprintf(current->comm,"khttpd - request");   
  21.     sock = (struct socket*)TMP;   
  22.     sk = sock->sk;      
  23.     sk->nonagle = 1;   
  24.     sk->linger  = 1;   
  25. ...   
  26.     error=0;   
  27.     Buffer = (char*) get_free_page(GFP_KERNEL);   
  28.     httptime = kmalloc(sizeof(struct http_time),GFP_KERNEL);   
  29.     atomic_dec(&InitCount);      
  30. ...   
  31.     memset(httptime,0,sizeof(struct http_time));   
  32.     while (1==1)   
  33.     {   
  34.         if (atomic_read(&AcceptCount)>KHTTPD_MAXACCEPT)  //判断是否拥有太多的子内核线程,其实就是http处理线程   
  35.             break;                                     //如果太多就不再启动本次的了   
  36.         newsock = sock_alloc();   
  37.         newsock->type = sock->type;   
  38.         sock->ops->dup(newsock,sock);   
  39.         ops = newsock->ops;   
  40.         atomic_inc(&AcceptCount);   
  41.         error = ops->accept(sock,newsock,0);   
  42.         atomic_dec(&AcceptCount);   
  43. ...   
  44.         error=HandleIncommingConnection(newsock,Buffer,&BufLen,httptime); //实际处理客户端请求   
  45.         if (error<0)   
  46.         {   
  47.             if (HandleUserSpace(newsock,Buffer,BufLen)<0)  //如果内核处理出错,那么返回给用户空间去处理,也就是交给用户空间的web服务器   
  48.                 ErrorXXX(newsock,-error);   
  49.         }   
  50. ...      
  51.     }   
  52.     free_page((unsigned long)Buffer);   
  53.     kfree(httptime);   
  54.     return 0;   
  55. }   
  56. int HandleIncommingConnection(struct socket *sock, char *Buffer, size_t *BufLen,struct http_time *httptime)   
  57. {   
  58.     struct msghdr        msg;   
  59.     struct iovec        iov;   
  60.     int             len;   
  61.     mm_segment_t        oldfs;   
  62.     struct http_header     Head;      
  63. ...//数据初始化   
  64.     oldfs = get_fs(); set_fs(KERNEL_DS);   
  65.     len = sock_recvmsg(sock,&msg,1024,0);   
  66.     set_fs(oldfs);   
  67. ...//出错处理,出错返回500   
  68.     /* Check if the request is finished */   
  69.     if ((len<4))   
  70.     {   
  71.         int len2;   
  72. ...//数据初始化   
  73.         len2 = sock_recvmsg(sock,&msg,1024,MSG_DONTWAIT);   
  74.         set_fs(oldfs);   
  75.         interruptible_sleep_on_timeout(&sock->wait,HZ*5);   
  76.         if (len2>0)   
  77.             len+=len2;   
  78.     }   
  79.     Buffer[len+1]=0;   
  80.     *BufLen     = len;   
  81.     ParseHeader(Buffer,len,&Head);   //解析头部   
  82.     if (Head.FileName[0]!=0)   
  83.     {   
  84.         struct file   * filp;   
  85. ...//出错处理,出错返回403   
  86.         filp = filp_open(Head.FileName,00,O_RDONLY); //打开客户端需要的文件   
  87. ...//出错处理,出错返回404      
  88.         if ((filp!=NULL)&&(filp->f_dentry!=NULL))   
  89.         {   
  90.             int FileLength,Permission;   
  91.             char *Header;   
  92. ...//权限判定,访问控制功能   
  93.             FileLength = (int)(filp->f_dentry->d_inode->i_size);   
  94.             if (Head.IMS[0]>0)   
  95.             {   
  96.                 time_t TIME;   
  97.                 TIME=mimeTime_to_UnixTime(Head.IMS);   
  98. ...//出错处理,出错返回304   
  99.             }   
  100.             Header = HTTPHeader(Head.FileName,FileLength,filp->f_dentry->d_inode->i_mtime,httptime);   
  101. ...//出错处理,出错返回500   
  102.             khttp_copy_filp_to_sock(filp,sock,FileLength,Header);   
  103.             if (Header)   
  104.                 kfree(Header);   
  105.             fput(filp);   
  106.         }   
  107.         return 200;          
  108.     }      
  109.     return -404;   
  110. }   
  111. 注 意以上除了成功返回200之外,别的出错码都不是返回给客户端的,而是在khttpd_child经过判断出错后,交给用户空间再处理一次,因为内核只负 责静态页面的推送而不管别的,因此内核处理不了而出错不代表用户空间的web服务器就处理不了,于是交给khttpd_child中调用的 HandleUserSpace:   
  112. int HandleUserSpace(struct socket *sock, char *ReceivedSoFar, size_t length)   
  113. {   
  114.     struct msghdr        msg;   
  115.     struct iovec        iov;   
  116.     int             len;   
  117.     mm_segment_t        oldfs;   
  118.     char            *Buffer;   
  119.     struct socket        *clientsock;   
  120.     /*    struct sockaddr_un    name;*/   
  121.     struct sockaddr_in     sin;   
  122.     int            error,count=0;   
  123.     Buffer = (char*) get_free_page(GFP_KERNEL);   
  124. ...//出错处理,真的返回500   
  125.     error = sock_create(PF_INET,SOCK_STREAM,IPPROTO_TCP,&clientsock);   
  126.     if (error<0)   
  127.         printk("Error during creation of socket; terminating\n");   
  128.     sin.sin_family         = AF_INET;   
  129.     sin.sin_addr.s_addr  = htonl(INADDR_LOOPBACK);  //注意,向回环接口也就是127.0.0.1接口发送客户端请求,这样写入的请求就会被用户空间的web服务器受到,某种意义上,这个khttpd更像 是一个web代理,在客户端和用户空间真正的web服务器之间的代理。   
  130.     sin.sin_port         = htons(KHTTPD_CLIENTPORT); //注意,这个端口一定要在用户空间的web服务器上配置   
  131.     error = clientsock->ops->connect(clientsock,(struct sockaddr*)&sin,sizeof(sin),0);  //连接真正的用户空间的web服务器,比如apache   
  132. ...      
  133.     SendBuffer(clientsock,ReceivedSoFar,length); //把客户端的请求发送真正的用户空间web服务器   
  134.     len=1;   
  135.     while ((len>0)&&(count<10000))   //这个循环中可以看出内核的khttpd就是一个代理   
  136.     {   
  137. ...//数据缓冲区初始化   
  138.         oldfs = get_fs(); set_fs(KERNEL_DS);   
  139.         len = sock_recvmsg(sock,&msg,4096,MSG_DONTWAIT);  //继续从客户端浏览器接受请求   
  140.         set_fs(oldfs);   
  141. ...   
  142.         if (len>0)   
  143.             SendBuffer(clientsock,Buffer,len);  //如果有请求则继续向用户空间的web服务器转发   
  144.         if (len<-100)   
  145.             break;    
  146. ...数据缓冲区初始化   
  147.         oldfs = get_fs(); set_fs(KERNEL_DS);   
  148.         len = sock_recvmsg(clientsock,&msg,4096,MSG_DONTWAIT);  //接受用户空间web服务器的回应   
  149.         set_fs(oldfs);   
  150.         if (len>0)   
  151.             SendBuffer(sock,Buffer,len);  //将回应转发给客户端   
  152. ...   
  153.         count++;   
  154.     }   
  155.     sock_release(clientsock);   
  156.     free_page((unsigned long)Buffer);   
  157.     return 0;   
  158. }   
  159. 注 意,这个函数返回了,如果返回值是负数,那么就说明用户空间的web服务器也处理不了,那就是真的发生了错误,于是就要真的进行错误返回了,其实就是调用 ErrorXXX(newsock,-error)函数,此时从HandleIncommingConnection返回的错误码还保留着:   
  160. void ErrorXXX(struct socket *sock, int Error)   
  161. {   
  162.     if (Error == 404)   
  163.     {      
  164.         Error404(sock);   
  165.         return;   
  166.     }   
  167. ...   
  168. }   
  169. void Error404(struct socket *sock)   
  170. {   
  171.      char Message[]=   
  172.     "HTTP/1.0 404 File Not Found\r\nServer: KHTTPD/0.0\r\nContent-type: text/html\r\n\r\nFILE NOT FOUND!!";   
  173.     SendBuffer(sock,Message,sizeof(Message));  //真正向客户端返回错误码。   
  174. }   
 以 上就是khttpd的大致框架了,很有意思的就是它在内核中的作用就是客户端和用户空间web服务器的代理,虽然它作为一个以加速为目的的web服务来说 已经不再需要,但是我倒是觉得它可以作为一个很好的过滤系统经过改进后继续存在,就像netfilter一样,但是netfilter拦截五元素很方便, 虽然也可以解析数据包的具体内容,但是那很耗时,而且大多数的运行netfilter的接收过滤的上下文都是软中断上下文,因此更加不确定,更不适合做像 从sk_buff中解析用户数据并且判定那些事,因此khttpd的框架可以作为很好的用户数据过滤框架在内核中存在,新的框架不再仅仅监听端口为80的 web服务,而是可以监听所有的端口,只要用户建立一个服务器套接字,那么都可以选择将此套接字加入到过滤列表中,可以通过ioctl调用很容易做到这一 点。然后可以在内核配置一张过滤表,存放每一个端口的过滤策略,这个表的格式可以参照iptables的结构设计,如果通过的话,可以将请求直接转发给用 户空间的套接字,如果没有通过验证,那么记录审计信息后返回错误。如此的设计可能没有必要,因为linux内核可能压根就不想管像用户数据那么具体的事 情,如果你非要往内核加入这个框架的话,那么过滤的策略的设置将是一场噩梦,毕竟用户数据是一个很庞大很不具体的概念,难道过滤敏感词汇吗?如果是的话, 估计敏感词汇都存在内核瞬间内存就满了,众口难调啊,即使这个想法不被应用了内核主线,那么很可能可以满足某个老板的需求,我们公司就在做一个网页防篡改 系统,我想我可以借鉴一下这个khttpd框架,然后改改,实现我们linux版的网页防篡改系统。       linux从来不在内核提供个别应用才会用到的策略,不允许策略污染机制,呵呵,即使在2.4内核时策略(khttpd)真的污染了那么一点点机制,然而 随着2.6内核的放出,仅有的污点还是被驱赶了,因为linux内核靠更好的机制就可以给用户空间提供更好的舞台,而想在这个舞台演得好需要成为好的舞者,这难道给用户编程带来了挑战,其实程序员需要的不是什么技巧,而是unix编程的哲学思想。

 

本文转自: http://blog.csdn.net/dog250  天地有如此静穆,我不能大笑而且歌唱

 

曾经读过n多个开源的代码,包括linux内核,apache,openssl,memcache,libevent,vsftpd,xinetd等等其 中我最喜欢的就是linux内核了,除了linux内核排第二的就是apache,本文我就把欣赏的心得记录下来。
      apache中我最喜欢的有两个模块,一个就是它的MPM,一个就是内存管理,mem使得它可以最高效得挖掘操作系统固有的优势,而不用为了接口兼容或者 接口一致而必须提供低效的模拟层,比如windows的进程创建过于低效,但是线程却是操作系统内置的,那么在windows平台上,apache就使用 线程作为mpm的机制,在linux2.6内核上,可以使用线程+进程的方式,而在linux2.4以前可以使用纯进程的经典unix方式来进行多处理。 mpm独立作为一个模块支撑着apache的运行,它是支撑模块,因此它是必须的,典型的,apache要么使用进程,要么使用线程来处理请求,但是绝对 不是进程就是为了稳定,而线程就是高效,而是不同的操作系统拥有不同的选择策略,它很灵活,管理员可以灵活地进行配置,当然线程+进程的方式拥有高效+稳 定的双重优势,最常用的mpm模块就是prefork和worker,具体的内容我就不讨论了,读源代码是最好的方式,另外还可以看一个博 客:http://blog.csdn.net/tingya。在mpm中还有一个很有趣的就是记分板,通过它负责管理的父进程就可以更快捷的管理实际进 行web服务的子进程,这实在是必用管道高效,因为记分板用的是共享内存机制,实际绕开了文件系统,具体工作的时候就是父进程查询记分板得到子进程的状 态,之所以可以如此轻松的完成这样的工作就是因为记分板的构造,每个子进程在记分板上都有一个插槽,父进程可以通过子进程的pid来检索到子进程的插槽从 而得到子进程的信息,子进程同样也需要记分板,它可以将需要通报给父亲的信息写到记分板然后由父进程读取,也可以读取父进程设置进去的信息,比如父进程需 要杀死自己的孩子,它不需要给孩子发送信号,而是通过写自杀信息到该孩子的记分板,等待该孩子看到这个信息以后采取自杀策略,这样的话不需要任何内核机 制,直接靠所有操作系统都提供的东西就可以完成父子通信,另外还可以进行兄弟通信,共享内存机制比管道或者信号来的更好,因为它减少了对操作系统的依赖, 提高了apache的跨平台特性,而且共享内存更加高效,可以做到父子进程内存的零拷贝。 
在通信方面,apache可以创建很多套接字排入一个队列或者放入一个池,每次只有一个监听,当监听到用户连接请求的时候,马上就可以将自己转换到服务套 接字,然后从队列或者池中选出下一个套接字进行监听,如此反复,这里讨论的仅仅是通信模型,当然,每个套接字可以是一个进程也可以是一个线程,归根结底要 想高效,还要用到上面提到的mpm机制,这里进程或者线程可以和套接字相对应,但是这种对应不是确定以及一定的,传统的通信模型在apache里面都可以 看到,它非常灵活,曾几何时,只有一个套接字进程监听,只要来一个马上fork一个进程,在子进程里面处理新的连接,但是那个日子一去不复返了,看看 apache吧,看完了你就知道如何进程多处理了。 
      apache的内存池模型令我陶醉,这是真的,我读过很多源码,可是读每个源码的时候,我都感觉大同小异,只要是网络方面的,必然是select,只要是 进程方面的,必然是...,可是当我开读apache的时候,我改变了这种想法,apache的内存管理就是与众不同,如果你仅仅把目光停留在它的内存管 理算法上,你会发现它和boost没有什么太大的区别,甚至有时都不如stl的,但是它却实现一种思想,一种事务的思想,你再也不用关注于编程细节,只要 知道你的程序在干什么事情就可以了,apache内存管理采用了内存池的思想,在进行内存分配之前,你必须首先创建一个内存池,然后所有的事情就不用你操 心了,一切都在池中进行,内存池的管理是apache里面进行的,用户不必涉及,在分配策略了,内部用到了很多链表等数据结构,还用到了很多巧妙的算法, 比如用内存的大小索引化,在查找的时候从最大的开始,减少了查找的脱靶次数从而提高了效率,但是这些都不是我这里要强调的。传统的看来,谁分配内存谁释放 这已经成了一个成文的规则,但是虽然规则如此严明的写了出来,代码的作者却还是造成了内存泄露,因为这种说法是以内存为中心的,而不是以人的思维为中心 的,因此程序员很容易犯一些错误,哪怕是最低级的错误,想想我们为何写程序,程序肯定是为了帮助人来完成一些事情,而这件事情肯定是很“人性化”的,比如 查询,比如删除,比如恐怖袭击,既然程序执行的都是人的事儿,那么为何不用点人的方式呢?我们人做事从来没有那么小的粒度,我们总是把一个事务作为一个整 体来看待,这不,apache里面的内存池就非常好,如果谁要用内存,那就只管用吧,用内存才是你的事,怎么管理内存不是你的事,而是你上级的事,这个现 象很好理解,举个例子,我在一家电脑公司任职,我平时要用电脑,打印机,打印纸等等,这些资源并不需要我自己管理,而是我的上级帮我管理,比如我的上级给 了我一台台式机和一台笔记本的份额,那么我根本不用管别的,份额内直接用就是了。在apache中,内存分配都在内存池中,一段代码代表一个业务逻辑,它 需要内存的话并不需要负责释放,而是这段代码的上级来负责释放,实际上apache中根本没有显式的释放给操作系统的内存释放,所有内存释放都是释放给内 存池的,如果要开始一个业务逻辑,那么必然需要先申请有一个内存池,在这个业务逻辑中所有的内存需求都从这个池中分配,业务逻辑本身并不需要和操作系统交 互,而由池的管理代码负责交互,内存释放时,释放到池中,这些内存还可以被该业务逻辑重新利用,等到这个业务逻辑结束的时候,内存池被销毁,池销毁的时 候,池中的内存将全部还给操作系统,这丝毫不用业务逻辑本身操任何心。内存池就好像一个封装,你只需要大胆的用内存就可以了,不够了池可以帮你从操作系统 要,多余了可以先存着,等到业务逻辑结束的时候,池销毁的时候,所有内存一并销毁,而销毁操作对用户即业务逻辑来说是不可见的。 
      apache的内存池管理方式使我想到了操作系统的内存管理方式,apache的libapr本身就好像一个操作系统,它分配资源不会被业务逻辑看到,业 务逻辑只需要等待结果就可以了,可是现在的任何操作系统却美誉这么“人性化”,可以这么说,apache是以业务逻辑即事务为中心的,其实它就是以人为本 的,而传统的操作系统由于收到强大的unix的影响,还是以任务为中心的,unix创立的时代是个必须以任务为中心的时代,但是现在还是那样的吗?虽然我 不很喜欢windows,但是我发现windows已经迈出了超越unix的第一步,当然linux早就超越了unix。 
     apache在我心中就是老二,linux内核永远是老大,不过如果单挑某个方面的话,不能否认apache远远超越了linux内核,在你想远离人群的时候,读读linux内核,当你想融入喜欢你的人的时候,读读apache,试试看。

2011 - 02 - 24

Apache体系结构ppt

Apache体系结构ppt

2006 - 01 - 19

Apache数组分析

3.1数组
3.1.1数组概述
数组是Apache中最经常也是最普通的数据结构,尽管C语言中已经提供了一定的数组功能,但是C语言数组运用到Apache中还存在下面的一些问题:
(1)、C语言中的数组在定义的时候就必须确定维数目,一旦确定,其长度就不可更改。但是Apache中很多情况并不知道数组中能够保存的最大数目,如果预定义的太大,可能会浪费过多的空间;定义的太小又可能不能满足系统要求;因此Apache中需要一种动态的具有弹性伸缩能力的数组,这样实际需要多少元素就分配多少元素;
(2)、C语言中提供了另外一种数组动态伸缩的方案,即就是使用malloc分配空间,将空间看作数组进行处理。不过对于malloc分配的空间只能保存一种类型的数据。如果需要保存其余类型,则需要重新分配。
Apache中的数组实现方案类似于STL或者Java中的Vector,其长度可以保持动态变化,而且其中可以存放不同类型的数据,包括基本数据类型到复合数据类型。
3.1.2数组结构
Apache中相关的数组结构都定义在文件apr_table.h和apr_table.c中了。apr_array_header_t结构是Apache的核心结构,其定义如下:
struct apr_array_header_t {
apr_pool_t *pool;
int elt_size;
int nelts;
int nalloc;
char *elts;
};
数组的结构示意图片可以用下图表示:
图2.1 Apache数组结构图
apr_array_header_t结构中的elts指针指向了实际的用malloc分配的空间,每个空间的大小为elt_size。由于数组元素的个数是可以动态变化的,因此结构中使用nalloc记录当前已经分配的数组中的元素个数。另外由于Apache中的数组元素并不是所有的都被用到,因此还需要nelts记录当前已经使用的元素个数,自然剩余的可以利用的数组元素个数为nalloc-nelts。分配数组所需要的所有空间都从内存池pool中获取。
3.1.3创建数组
数组的创建通过调用apr_array_make实现,不过其内部主要工作由make_array_core实现:
static void make_array_core(apr_array_header_t *res, apr_pool_t *p,int nelts, int elt_size, int clear)
{
if (nelts < 1) {
nelts = 1;
}
if (clear) {
res->elts = apr_pcalloc(p, nelts * elt_size);
}
else {
res->elts = apr_palloc(p, nelts * elt_size);
}
res->pool = p;
res->elt_size = elt_size;
res->nelts = 0; /* No active elements yet... */
res->nalloc = nelts; /* ...but this many allocated */
}
APR_DECLARE(apr_array_header_t *) apr_array_make(apr_pool_t *p,int nelts, int elt_size)
{
apr_array_header_t *res;
res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t));
make_array_core(res, p, nelts, elt_size, 1);
return res;
}
apr_array_make函数负责主要用于分配apr_array_head_t头结构,而make_array_core则主要负责创建和初始化res元素数组。Res数组的大小中每一个元素的大小为elt_size,总元素个数为nelts,因此分配的总空间为nelts*elt_size,空间分配后继续初始化apr_array_header_t结构中的成员。如果clear为1,则调用apr_pcalloc进行分配,否则调用apr_palloc,两者唯一的区别就是apr_pcalloc会对分配的空间进行初始化清零;而apr_palloc则不会初始化。
除了从头开始创建一个新的数组之外,APR中还允许使用apr_array_copy将一个数组拷贝到一个新的数组:
APR_DECLARE(apr_array_header_t *) apr_array_copy(apr_pool_t *p,const apr_array_header_t *arr)
Arr是需要拷贝的源数组,拷贝后的数组由函数返回。拷贝函数内部首先定义一个arp_array_header_t的变量res,从内存池p中为其分配足够的空间,然后调用make_array_core生成一个与arr数组大小相同的数组,再调用memcpy逐一进行内存拷贝。然后返回该空间。
3.1.4元素压入和弹出
Apache中对数组元素的操作类似于堆栈,只允许弹出(pop)和压入(push)两种,因此你无法象在C语言中随意的访问数组中的元素。
当数据需要保存到数组中的时候,通过函数apr_array_push函数进行,其原型声明如下:
APR_DECLARE(void *) apr_array_push(apr_array_header_t *arr)
arr是需要保存到数组中的元素。元素的保存按照从左到右,即从低索引到高索引依次存储,不可能出现C语言中的随机保存的情况,因此从这个角度而言,数组称之为堆栈更合适。
在将元素压入数组之前函数首先检查数组中是否有可用的空闲空间;如果有剩余可用空间,其将直接将元素拷贝到第nelts+1数组的位置,调整nelts为nelts++,同时返回该元素。如果没有,即意味着当前数据中nalloc个空间都已经被使用,即nelts=nalloc,那么此时必须扩大数组空间用来保存压入的数据。不过数组空间的增加并不是需要几个就增加几个,而是遵循下面的批量增加原则:
■ 如果压入数据之前nalloc为0,即尚未分配任何空间,那么此时只创建大小为elt_size的空间,即一个元素的空间。
■ 如果当前元素压入之前,系统分配的nalloc个单元都已经被使用完毕,那么此时将直接批量一次性将空间扩大到nalloc*2个元素空间。
一旦分配完这些空间,第一个可用的空闲空间则是第nalloc+1个元素。此时只需要将需要压入的元素拷贝到该地址空间即可,拷贝完之后,将elt_size加一,并返回新元素的地址。
与apr_array_push类似,Apache提供了另外的一个版本的压入函数apr_array_push_noclear,该函数与apr_array_push函数功能基本类似,其唯一区别正如函数名称,在于“noclear”。 apr_array_push在产生新的空闲块,压入新元素之前调用memset对新内存块进行清零操作,而“noclear”函数则省去了这一步。
与元素压入匹配,从数组中取元素通过函数apr_array_pop进行,其函数声明如下:
APR_DECLARE(void *) apr_array_pop(apr_array_header_t *arr)
元素弹出遵循先进先出的弹出原则,因此被弹出的元素永远都是第nelts个,除非当前的数组为空,不在有任何的有效数据。返回语句可以简化为return arr->elts + (arr->elt_size * (--arr->nelts));
3.1.5数组合并
Apache中使用apr_array_cat将两个元素进行合并,函数定义如下:
APR_DECLARE(void) apr_array_cat(apr_array_header_t *dst,const apr_array_header_t *src)
函数将数组src添加到数组dst的末尾。函数首先检查当前的空闲单元能不能容下src数组,这里容纳的概念并不是容纳src中的nalloc个元素,而仅仅是容纳下src中的nelts个非空闲单元,并不包括nalloc-nelts空闲单元,因此函数需要比较的是(dst->nalloc) - (dst->nelts) >= (src->nelts),而不是(dst->nalloc) - (dst->nelts) >= (src->nalloc)。如果能够容纳,则没有必要分配新的空间,直接内存拷贝就可以了。
如果dst中所有的空闲空单元都不够存放src中的空闲单元,那么此时毫无疑问dst需要分配新的空间,分配算法如下:
1)、如果dst中元素个数为零,此时,将产生一个新的空间。
2)、如果dst中元素个数nalloc不为零,则首先产生nalloc*2个空闲空间。
3)、尽管如此,如果src的非空闲元素实在太多,而dst本身空闲空间很小,那么即使一次产生 nalloc个空闲块也不一定能够盛放src中的元素。唯一的办法就是不停的产生新的空闲块,每次产生后的总块数都是当前的一倍,直到空闲块总数能够容纳src中的非空闲元素为止。这真是下面的代码所做的事情:
if (dst->nelts + src->nelts > dst->nalloc) {
int new_size = (dst->nalloc <= 0) ? 1 : dst->nalloc * 2;
while (dst->nelts + src->nelts > new_size) {
new_size *= 2;
}
}
一旦确定确定需要产生的空闲块的总数,函数将一次性从内存池dst->pool中申请。然后将src中的数据拷贝空间空间即可。

关于作者
张中庆,目前主要的研究方向是嵌入式浏览器,移动中间件以及大规模服务器设计。目前正在进行Apache的源代码分析,计划出版《Apache源代码全景分析》上下册。Apache系列文章为本书的草案部分,对Apache感兴趣的朋友可以通过flydish1234 at sina.com.cn与之联系!

如果你觉得本文不错,请点击文后的“推荐本文”链接!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值