转载注明出处!
版权所有,yacsha(wangcheng711@gmail.com)
http://blog.youkuaiyun.com/yacsha/
2007-05-17
第2章 Apache平台和结构
apache作为常驻后台程序运行(windows下是services,unix下是deamon),这样可以加快服务速度和节省服务器资源.作为一个实际运作中的服务程序,我们需要让它随系统启动,常驻系统之中.早期apache版本运行在inetd模式 (对每一个请求创建一个进程来服务),这种模式不太适合实际中使用.
2.1 概要
apache http服务器由一个小核心和一系列模块组成.这些模块可以以静态方式编译到服务进程程序中,也可以以动态库的形式加载,动态模块一般存放在/modules/或者/libexec/目录.另外apache http服务程序需要依靠apr库(注…..)运行.apr库专门用来提供跨平台.
apache结构
因此模块的运行不需要依靠具体的平台.mpm(多进程模式)模块是一个特别的模块,用来针对专一的平台来优化apache. mpm模块是唯一一个需要和操作系统交道的模块,其他模块应该通过apr库来完成自己的功能.
2.2 两个阶段
apache启动服务有两个阶段.初始化和运行.以root方式随系统启动,解析配置文件,加载模块,初始化一些系统资源,例如日志文件,共享内存段,数据库连接.对正常的操作,apache放弃自己特权用户级别,启动非特权用户用来接受用户的服务请求.这种基本安全方法用来阻止apache(或者模块,脚本)中一些简单错误而导致的系统级别的安全问题.像用malware对ms iis进行溢出攻击的”code red(红色代码)”和”Nimda(尼姆达)”病毒.
这两个阶段的操作暗示着应用程序的结构,首先,任何需要系统权限的程序都必须在系统启动时候运行(译者注:这个好像有问题???不随系统启动就拿不到系统权限??).第二,在程序启动的时候进行所有的初始化,这是一个好方法.减少在接受请求的时候进行初始化.这些耗资源和拖慢速度的操作都在程序启动的时候完成, 相比apache以inetd或者tcpserver模式运行是有效率的.
在启动的时候执行两次配置代码是一个没有直觉,奇怪的结构(不包括重新启动服务),第一次,检查配置是不是有效(最少能让apache启动),第二次是在启动服务的时候,就是我们上面说到的第二个阶段,大多数模块都能忽略这一阶段的初始化(注:apache会以非特权用户的方式启动服务进程),通过使用apr缓存池可以避免资源泄露,这些对一些模块有一些提示,例如,一个动态加载的模块只希望运行一次初始化代码,因此我们必须使用一种方法通过设置和检查全局的一个标志变量来确保关键变量只被初始化了一次.
2.2.1 初始化阶段
这个阶段用来读取配置文件,加载模块,初始化需要的资源,每个模块可能有自己需要的资源,在这个时候初始化自己需要的资源.在初始化阶段, apache以一个单进程,单线程,完全控制系统的方式运行.
2.2.1 .1 配置
httpd.conf文件是apache的主要配置文件,术语规范,第三方apache组织,像提供rpm,deb包.可能使用一个不同命名表,另外httpd.conf文件可能是单一文件,也可能是多个文件.通过使用include指令来包含不同的配置文件.一些组织有非常难懂的配置,例如对Debian GNU/Linux的apache配置,需要对Debian非常熟悉.本书不是用来谈论这种设计的优缺点,因此我们仅仅简单的把这个配置文件叫做httpd.conf.
httpd.conf文件是一个纯文本文件,在程序初始化的时候被一行一行的解析,该文件由指令,容器,注释组成.空白行是允许的,不会对配置起任何影响.
指令
httpd.conf文件中大多数内容是指令.一个指令可以没有参数,也可以有多个参数,参数之间用空格隔开.每一个指令有自己的语法格式,不同的指令允许不同的参数个数和参数类型(有字符串 数字 枚举 布尔 或者文件名).核心和一些模块有自己支持的指令,我们在第9章中详细讨论.
例如:
LoadModule foo_module modules/mod_foo.so
这个指令由mod_so支持,用来加载一个模块.第一个参数是模块名(字符串,数字字符).第二个参数是文件名.可以是绝对路径也可以是相对路径.
DocumentRoot /usr/local/apache/htdocs
这个指令由核心模块支持,用来设置网页根目录.
SetEnv hello “Hello, World!”
这个指令由mod_env支持,用设置环境变量.注意,第二个参数包含空格,我们必须用双引号包含起来.
Choices On
这个指令由mod_choices(第6章)支持,用来激活模块的选项.
容器
一个容器是一个特定集合的指令.通过一系列简单的尖括号来归类,容器在语义上和其他指令不同,指令的开始和结束分别独立占一行.有效的指令落在开始和结束的中间.例如由内核模块实现的<VirtualHost>指令.如以下指令
<VirtualHost 10.31.2 .139>
ServerName www.example.com
DocumenRoot /usr/www/example
ServerAdmin webmaster@example.com
CustomLog /var/log/www/example.log
</VirtualHost>
容器提供作用域的功能,里面的指令只在该作用域内生效.在上面这个例子中,指令只对访问www.example.com域名时候有效.该容器可以被嵌套,除非明确的指示不能嵌套.所有用到的指令,包括容器都是在具体的上下文中生效.
注释
任何一行以#号开头都被认为是注释.
# this line is a comment
一般行中的#不会被认为是注释,除非模块明确的支持该指令.
一个模块支持的指令在模块没有被加载的时候不会生效.apache在解析这个指令的时候会遇到语法错误.因此mod_so必须以静态的方式加载,这点非常重要的!当你开发一个新模块的时候,没有LoadModule的支持,在你修正你编写的模块的时候,必须重新编译整个服务器程序!
2.2.2 运行阶段
在初始化阶段结束的时候,程序的控制转向多进程模块(mpm)(见2.3章节),mpm在系统级别管理apache的操作.根据当前系统来管理进程池或者线程池和一些应用限制(例如对特定情节的应用).启动进程作为控制者,管理一系列子工作例程.这些工作例程用来接受用户的请求.控制者用来创建新的子工作例程,移除多余的工作例程,通过信号指示来停止或者重启服务.
由于mpm的结构,我们不能在确定的范围内描述运行阶段.尽管标准的mpm以工作例程的方式使用,但是没有被限制只能使用这一种方式使用mpm..原理上另外一个mpm能够在系统层上实现完全不同的服务器结构.
2.2.3 停止
这里没有停止阶段,但是有一些清除工作需要完成在服务停止的时候,在第3章中描述,当apache停止的时候,所有注册的清除操作都会被执行.
2.3 多进程处理模块(mpm)
在初始化结束阶段,所有的配置也被解析后,apache的控制转到mpm控制模块,mpm提供apache服务程序和操作系统之间的接口,该模块的主要职责是优化apache适应当前的操作系统平台,确保apache能够足够安全和有效率.
通过名字我们能看出,mpm自身也是一个模块.但是mpm是一个唯一的系统层模块(因此开发mpm已经超出本书关于应用程序开发),每一个apche实例在编译的时候必须包含一个准确唯一的mpm模块.
2.3.1 为什么有多种mpm方式?
老版本的NCSA server和apache 1在unix环境下产生.是一个多进程服务器,一个服务例程服务一个请求.如果当前并发客户访问数量大于服务进程数,apache会产生相应的服务进程数来完成当前请求.在正常情况下,apache会维护一定数量的服务进程来处理用户的请求.
尽管在unix类(注1)系统下该调度方法能很好的工作,但是在windows下显得不够有效率.windows下产生一个进程是非常费时的过程.因此让apache在各个平台上都有效率,我们需要另外一些方法.被apache2采纳的方法是让内核能够处理以模块方式插入的mpm,能被优化适应不同的环境.mpm的结构也允许不同的apache模块在一个操作系统平台下共存,能够给用户提供各种使用用途.
----------------------------------------------------------------------------------------------------------------------
注1:在本书中提到的unix类,包括unix系统和其他posix操作系统,例如linux和macosx
在实际中,仅仅unix类操作系统提供了一个有用的选择:其他支持平台(Windows,Netware,OS/2,BeOS)只有一个单一的mpm优化.在unix平台上,apache2.2目前已经有两种产品化的,有效率的mpm方式(prefork和worker),第三种(event方式)被认为是稳定的除了不支持ssl,另外其他一些实验中的mpm方式不适合加入产品中.第三方mpm模块也可以使用.
2.3.2 unix类的mpm方式
prefork mpm和apache1.x版本中的mpm本质相似,没有线程.在所有情况下都很安全.对运行安全没有线程(non-thread-safe)模式软件,例如php,这是唯一的安全配置选项.对一些扩展程序,包括在apache1.3上非常流行的扩展程序(例如,简单静态文件,cgi脚本),这种mpm模式是最好的选择.
worker mpm是一种线程模式,有以下优点,耗用内存低(对繁忙的服务很重要),在特定的服务类型上相比prefork有更好的测量性.我们稍后会讨论其中一些情况在我们介绍sql数据库支持和mod_dbd.
以上两种mpm方式的稳定性取决于服务器的繁忙程度.尽管http的keepalive能减少tcp连接和网络负载.但是keepalive和一个具体的服务进程或者线程绑定,结果在一个繁忙的机器上没有更多的资源来产生更多的进程或者线程来保持keepalive服务.event mpm是一种新方式,用来解决把服务进程从连接中分身出来.事实证明event mpm方式是最有用的,在有很高访问率,服务处理的速度之快,导致一个系统能产生最大线程数成为关键瓶颈.在一个以worker mpm方式工作的繁忙服务器上可能能承受每秒好几万的访问量(例如在大型新闻服务站点的高峰时候可能发生),但是event mpm可能帮助我们处理高负载更容易.注意event mpm不能在安全http(https)访问下工作.
这里也有一些针对unix实验中的的mpm方式,在本书编写过程中,有的在继续开发,有的已经停止开发了.有可能实现,也有可能不会被实现.perchild mpm方式承诺有很好的处理多请求的能力.以不用的用户id在不同的虚拟主机下运行.一些可选择的mpm也提供的相同的功能.包括第三方的Metux和Peruser mpm方式,还有mod_ruid(只支持linux),为了运行外部程序,可选择的有fastcgi/mod_fcgid和suexec(CGI).作者对第三方的解决方案没有相应的了解,不能做出相应的建议.
2.3.3 与工作相关的mpm方式和操作系统
一句话总结:mpm方式对应用程序不可见,我们忽略它!
apache的应用开发者不需要知道mpm的细节.mpm的内部不是api的一部分,简单易懂的提供在实际操作(线程安全,进程安全,代码重入)中的基本原理,这些将会在第4章中简单描述.非常接近在泛泛而谈开发平台无关代码的问题.事实上,在开发扩展程序的时候考虑mpm比考虑操作系统有时候更有好处.
有时候扩展程序适应一些mpm比适应其他东西更好些.例如,数据驱动或者加载平衡程序得益于连接池(在本书稍后讨论),在thread mpm方式中.相比之下,产生子进程(原始的cgi实现或者mod_ext_filter)会产生巨大开销在一个基于线程的程序中.因此在prefork mpm方式下工作的更好. 除非有强迫性的原因在限制程序在工作在使用不理想的mpm方式下.
如果你希望让apache运行在现在还不支持的系统上,主要的任务是增加对平台的支持到apr库中.apr库用来提供操作系统层的支持.当前mpm可以需要也可以不需要.但是好像提供一个好性能的服务程序比仅仅能运行的服务程序要好很多,总观apache,这是一个系统界别的任务,它已经超出一本应用程序开发书籍的讲述范围.
2.4 基本概念和数据结构
想在apache平台之上做开发,我们需要对基本的web服务器的操作和apache内部的核心结构有大致的了解.最重要的是server, tcp 连接, http请求.相比应用程序的结构,第四个是apache基本对象是进程.操作系统的一个执行单元.每一个基本单元由一个核心数据结构表示.在httpd.h头文件中定义.在扩展程序的开发中,我们遇到的一些核心结构完全不受mpm的约束.
在描述这些核心结构之前,我们需要介绍贯穿在整个apache当中,而且和体系结构紧密关联的概念.
apr pools(apr_pool_t)是apache中资源管理的核心.无论何时动态分配资源,对pool注册一个清除回调,当他们不再需要的时候确保系统资源能够被释放.pools绑定资源到一个内核对象的生命周期当中.我们会在第3章中深入讨论pools
配置记录被每一个模块使用,绑定自己的数据到一个内核数据当中,内核数据结构包括配置vectors(ap_conf_vector_t),在vectors中每一个模块有自己的入口,这里有两种使用方式,设置和获取全局的配置数据.保存临时数据到临时的对象中.在一个模块中应该尽量避免不安全的静态变量和全局变量.我们将在第4和第9章中讨论.
已经简单介绍完pools和配置数据,我们现在来解开apache核心结构.按照对大多数模块重要性的排列,它们是:
request_rec
server_rec
conn_rec
process_rec
在应用程序开发中,到目前为止,前两种是经常遇到的.
2.4.1 request_rec
一个request_rec对象,在apache接受连接请求的时候创建.在处理完请求之后马上销毁该对象. request_rec对象被传递到位于处理请求链(在第5和6章中讨论)每一个模块中的处理例程中,这个对象保存所有涉及处理该请求的内部数据.也包括apache用来维护中间状态和客户端信息的一系列字段:
请求池,管理在处理请求周期中的对象.
配置记录vector,静态请求配置(在httpd.conf或者.htaccess中指定的目录访问权限)
在处理请求过程中临时的配置记录vector
http输入头,输出头,错误信息头表
apache环境变量表(像SSI,CGI,mod_rewrite,PHP脚本处理扩展中用到的环境变量),还有一个相似的记录请求数据的提示表,该表对脚本不可见.
指向其他相关对象的指针.包括connection,server和任何一个相关的请求对象.
指向输入输出过滤链(在第8章中讨论)
URI请求和其中间解析形式.包括处理例程(见第5章)和文件系统映射(见第6章)
这是在httpd.h文件中的完整定义:
/** A structure that represents the current request */
struct request_rec {
/** The pool associated with the request */
apr_pool_t *pool;
/** The connection to the client */
conn_rec *connection;
/** The virtual host for this request */
server_rec *server;
/** Pointer to the redirected request if this is an external redirect */
request_rec *next;
/** Pointer to the previous request if this is an internal redirect */
request_rec *prev;
/** Pointer to the main request if this is a sub-request
* (see http_request.h) */
request_rec *main;
/* Info about the request itself... we begin with stuff that only
* protocol.c should ever touch...
*/
/** First line of request */
char *the_request;
/** HTTP/0.9, "simple” request (e.g., GET /foo/n w/no headers) */
int assbackwards;
/** A proxy request (calculated during post_read_request/translate_name)
* possible values PROXYREQ_NONE, PROXYREQ_PROXY, PROXYREQ_REVERSE,
* PROXYREQ_RESPONSE
*/
int proxyreq;
/** HEAD request, as opposed to GET */
int header_only;
/** Protocol string, as given to us, or HTTP/0.9 */
char *protocol;
/** Protocol version number of protocol; 1.1 = 1001 */
int proto_num;
/** Host, as set by full URI or Host: */
const char *hostname;
/** Time when the request started */
apr_time_t request_time;
/** Status line, if set by script */
const char *status_line;
/** Status line */
int status;
/* Request method, two ways; also, protocol, etc. Outside of protocol.c,
* look, but don’t touch.
*/
/** Request method (e.g., GET, HEAD, POST, etc.) */
const char *method;
/** M_GET, M_POST, etc. */
int method_number;
/**
* ‘allowed’ is a bit-vector of the allowed methods.
*
* A handler must ensure that the request method is one that
* it is capable of handling. Generally modules should DECLINE
* any request methods they do not handle. Prior to aborting the
* handler like this, the handler should set r->allowed to the list
* of methods that it is willing to handle. This bitvector is used
* to construct the "Allow:" header required for OPTIONS requests,
* and HTTP_METHOD_NOT_ALLOWED and HTTP_NOT_IMPLEMENTED status codes.
*
* Since the default_handler deals with OPTIONS, all modules can
* usually decline to deal with OPTIONS. TRACE is always allowed;
* modules don’t need to set it explicitly.
*
* Since the default_handler will always handle a GET, a
* module which does *not* implement GET should probably return
* HTTP_METHOD_NOT_ALLOWED. Unfortunately this means that a Script GET
* handler can’t be installed by mod_actions.
*/
apr_int64_t allowed;
/** Array of extension methods */
apr_array_header_t *allowed_xmethods;
/** List of allowed methods */
ap_method_list_t *allowed_methods;
/** byte count in stream is for body */
apr_off_t sent_bodyct;
/** body byte count, for easy access */
apr_off_t bytes_sent;
/** Last modified time of the requested resource */
apr_time_t mtime;
/* HTTP/1.1 connection-level features */
/**Sending chunked transfer-coding */
int chunked;
/** The Range: header */
const char *range;
/** The "real" content length */
apr_off_t clength;
/** Remaining bytes left to read from the request body */
apr_off_t remaining;
/** Number of bytes that have been read from the request body */
apr_off_t read_length;
/** Method for reading the request body
* (e.g., REQUEST_CHUNKED_ERROR, REQUEST_NO_BODY,
* REQUEST_CHUNKED_DECHUNK, etc.) */
int read_body;
/** reading chunked transfer-coding */
int read_chunked;
/** is client waiting for a 100 response? */
unsigned expecting_100;
/* MIME header environments, in and out. Also, an array containing
* environment variables to be passed to subprocesses, so people can
* write modules to add to that environment.
*
* The difference between headers_out and err_headers_out is that the
* latter are printed even on error, and persist across internal redirects
* (so the headers printed for ErrorDocument handlers will have them).
*
* The ‘notes’ apr_table_t is for notes from one module to another, with no
* other set purpose in mind...
*/
/** MIME header environment from the request */
apr_table_t *headers_in;
/** MIME header environment for the response */
apr_table_t *headers_out;
/** MIME header environment for the response, printed even on errors and
* persist across internal redirects */
apr_table_t *err_headers_out;
/** Array of environment variables to be used for subprocesses */
apr_table_t *subprocess_env;
/** Notes from one module to another */
apr_table_t *notes;
/* content_type, handler, content_encoding, and all content_languages
* MUST be lowercased strings. They may be pointers to static strings;
* they should not be modified in place.
*/
/** The content-type for the current request */
const char *content_type; /* Break these out -- we dispatch on ‘em */
/** The handler string that we use to call a handler function */
const char *handler; /* What we *really* dispatch on */
/** How to encode the data */
const char *content_encoding;
/** Array of strings representing the content languages */
apr_array_header_t *content_languages;
/** variant list validator (if negotiated) */
char *vlist_validator;
/** If an authentication check was made, this gets set to the user name. */
char *user;
/** If an authentication check was made, this gets set to the auth type. */
char *ap_auth_type;
/** This response cannot be cached */
int no_cache;
/** There is no local copy of this response */
int no_local_copy;
/* What object is being requested (either directly, or via include
* or content-negotiation mapping).
*/
/** The URI without any parsing performed */
char *unparsed_uri;
/** The path portion of the URI */
char *uri;
/** The filename on disk corresponding to this response */
char *filename;
/** The true filename, we canonicalize r->filename if these don’t match */
char *canonical_filename;
/** The PATH_INFO extracted from this request */
char *path_info;
/** The QUERY_ARGS extracted from this request */
char *args;
/** finfo.protection (st_mode) set to zero if no such file */
apr_finfo_t finfo;
/** A struct containing the components of URI */
apr_uri_t parsed_uri;
/**
* Flag for the handler to accept or reject path_info on
* the current request. All modules should respect the
* AP_REQ_ACCEPT_PATH_INFO and AP_REQ_REJECT_PATH_INFO
* values, while AP_REQ_DEFAULT_PATH_INFO indicates they
* may follow existing conventions. This is set to the
* user’s preference upon HOOK_VERY_FIRST of the fixups.
*/
int used_path_info;
/* Various other config info which may change with .htaccess files.
* These are config vectors, with one void* pointer for each module
* (the thing pointed to being the module’s business).
*/
/** Options set in config files, etc. */
struct ap_conf_vector_t *per_dir_config;
/** Notes on *this* request */
struct ap_conf_vector_t *request_config;
/**
* A linked list of the .htaccess configuration directives
* accessed by this request.
* N.B.: always add to the head of the list, _never_ to the end.
* That way, a sub-request’s list can (temporarily) point to a parent’s list
*/
const struct htaccess_result *htaccess;
/** A list of output filters to be used for this request */
struct ap_filter_t *output_filters;
/** A list of input filters to be used for this request */
struct ap_filter_t *input_filters;
/** A list of protocol level output filters to be used for this
* request */
struct ap_filter_t *proto_output_filters;
/** A list of protocol level input filters to be used for this
* request */
struct ap_filter_t *proto_input_filters;
/** A flag to determine if the eos bucket has been sent yet */
int eos_sent;
/* Things placed at the end of the record to avoid breaking binary
* compatibility. It would be nice to remember to reorder the entire
* record to improve 64-bit alignment the next time we need to break
* binary compatibility for some other reason.
*/
};
2.4.2 server_rec
server_rec定义了一个逻辑web服务器结构,如果在使用虚拟主机,每一个虚拟主机都有自己的server_rec结构,并且和其他主机独立, server_rec在服务器初始化的时候创建,直到服务器关闭的时候被销毁. server_rec没有自己的缓冲池,相反,服务器资源需要从所有服务器共享的进程池中分配.有一个配置向量,也有包括服务器名字,定义,资源,限制,日志信息的服务器资源.
对程序员来讲,server_rec是第二重要的结构,在request_rec之后.在整个讨论模块编程的过程中, server_rec有着显著的重要作用.
下面是在httpd.h文件中的完整定义
/** A structure to store information for each virtual server */
struct server_rec {
/** The process this server is running in */
process_rec *process;
/** The next server in the list */
server_rec *next;
/** The name of the server */
const char *defn_name;
/** The line of the config file that the server was defined on */
unsigned defn_line_number;
/* Contact information */
/** The admin’s contact information */
char *server_admin;
/** The server hostname */
char *server_hostname;
/** for redirects, etc. */
apr_port_t port;
/* Log files -- note that transfer log is now in the modules... */
/** The name of the error log */
char *error_fname;
/** A file descriptor that references the error log */
apr_file_t *error_log;
/** The log level for this server */
int loglevel;
/* Module-specific configuration for server, and defaults... */
/** true if this is the virtual server */
int is_virtual;
/** Config vector containing pointers to modules' per-server config
* structures. */
struct ap_conf_vector_t *module_config;
/** MIME type info, etc., before we start checking per-directory info */
struct ap_conf_vector_t *lookup_defaults;
/* Transaction handling */
/** I haven't got a clue */
server_addr_rec *addrs;
/** Timeout, as an apr interval, before we give up */
apr_interval_time_t timeout;
/** The apr interval we will wait for another request */
apr_interval_time_t keep_alive_timeout;
/** Maximum requests per connection */
int keep_alive_max;
/** Use persistent connections? */
int keep_alive;
/** Pathname for ServerPath */
const char *path;
/** Length of path */
int pathlen;
/** Normal names for ServerAlias servers */
apr_array_header_t *names;
/** Wildcarded names for ServerAlias servers */
apr_array_header_t *wild_names;
/** limit on size of the HTTP request line */
int limit_req_line;
/** limit on size of any request header field */
int limit_req_fieldsize;
/** limit on number of request header fields */
int limit_req_fields;
};
2.4.3 conn_rec
conn_rec是apache内部用来表示tcp连接的对象,在apache接受客户连接请求的时候创建,在连接被关闭的时候,对象被销毁.在通常情况下,一个连接服务一个或者更多请求,因此一个或者更多request_rec被创建对应一个conn_rec.大多数应用程序重点放在请求上,忽略conn_rec,但是协议模块和连接层过滤器需要使用conn_rec,有时候模块在一些任务中使用它,例如,优化在一个http keepalive(持续性连接)生命周期中的资源使用.
conn_rec没有配置信息,但是有一个配置向量为连接的临时数据和连接资源池,也有连接输入输出过滤链,加上描述tcp连接的描述信息.
清晰的分清请求和连接是非常重要的.前者往往是后者的子集.apache明白的展示这两个作为独立的对象,除了一个重要的例外情况,我们在第8章讨论讨论连接过滤器的时候会涉及到这个情况.
下面是在httpd.h文件中的完整定义
/** Structure to store things which are per connection */
struct conn_rec {
/** Pool associated with this connection */
apr_pool_t *pool;
/** Physical vhost this conn came in on */
server_rec *base_server;
/** used by http_vhost.c */
void *vhost_lookup_data;
/* Information about the connection itself */
/** local address */
apr_sockaddr_t *local_addr;
/** remote address */
apr_sockaddr_t *remote_addr;
/** Client's IP address */
char *remote_ip;
/** Client's DNS name, if known. NULL if DNS hasn't been checked;
* "" if it has and no address was found. N.B.: Only access this through
* get_remote_host() */
char *remote_host;
/** Only ever set if doing rfc1413 lookups. N.B.: Only access this through
* get_remote_logname() */
char *remote_logname;
/** Are we still talking? */
unsigned aborted:1;
/** Are we going to keep the connection alive for another request?
* @see ap_conn_keepalive_e */
ap_conn_keepalive_e keepalive;
/** Have we done double-reverse DNS? -1 yes/failure, 0 not yet,
* 1 yes/success */
signed int double_reverse:2;
/** How many times have we used it? */
int keepalives;
/** server IP address */
char *local_ip;
/** used for ap_get_server_name when UseCanonicalName is set to DNS
* (ignores setting of HostnameLookups) */
char *local_host;
/** ID of this connection; unique at any point in time */
long id;
/** Config vector containing pointers to connections per-server
* config structures */
struct ap_conf_vector_t *conn_config;
/** Notes on *this* connection: send note from one module to
* another. Must remain valid for all requests on this conn. */
apr_table_t *notes;
/** A list of input filters to be used for this connection */
struct ap_filter_t *input_filters;
/** A list of output filters to be used for this connection */
struct ap_filter_t *output_filters;
/** Handle to scoreboard information for this connection */
void *sbh;
/** The bucket allocator to use for all bucket/brigade creations */
struct apr_bucket_alloc_t *bucket_alloc;
/** The current state of this connection */
conn_state_t *cs;
/** Is there data pending in the input filters? */
int data_in_input_filters;
};
2.4.4 p rocess_rec
不像先前讨论的核心结构, process_rec对象相比web结构对象更像操作系统对象.应用程序需要关注process_rec结构仅仅在当他们操作有着服务器生命周期的资源和在进程池服务所有的server_rec对象(在server_rec中通过s->process->pool访问)时候.结构定义在httpd.h文件中,我们这里不再重复列出.
2.5 其他关键API
应用程序开发者除了使用定义这些核心结构的httpd.h头文件,还使用许多API头文件.能够通过名字约定,把这些头文件宽松的分为几类.
• ap_ 头文件概括地定义了低级别的API元素,通常情况,间接地被在其他头文件中通过包含使用(使用的情况也不多).
• http_ 头文件定义了应用程序开发者感兴趣的大多数关键APIs. 这些API也被导出到脚本语言通过模块,例如mod_perl和mod_python模块.
• util_ 头文件定义的API元素比ap_ 头文件高一级,但是很少被应用程序模块直接使用. 有两个例外情况,定义脚本和过滤API的util_script.h和 util_filter.h文件各自被模块普遍地使用.
• mod_ 头文件定义被可选模块支持的APIs.使用这些APIs可能会产生依赖,在第10章讨论好的实践.
• apr_ 头文件定义了APR APIs. APR库对服务器来讲既是是外部的,又是基本的.对有实际功能的模块,APR库是必须的(直接地或间接地).在第3章讨论APR库.
• 其他头文件仅仅概括地定义了系统级别的APIs.
• 第三方APIs可能遵循相似的约定(例如, mod_ 头文件)或者采纳他们自己的约定.
在前面提到,应用模块开发的主要APIs是http_* 头文件.
• http_config.h——定义配置API,包括配置数据结构,配置向量及相应的存取器和特别在第9章的主要APIs.也定义了自己的模块数据结构及相应的存取器,处理器(内容产生器)拦截,大多数模块都需要这个文件.
• http_connection.h——定义(小)TCP连接,包括连接级的拦截,大多数模块访问连接状态通过conn_rec对象.因此应用程序模块很少需要这个文件的API.
• http_core.h——定义了被apache核心导出的各种混杂的APIs,例如对request_rec对象进行存取的功能函数,也包括为特定模块导出的APIs,例如支持mod_perl模块的配置.这个头文件很少被应用程序模块需要.
• http_log.h——定义错误日志API和管道日志.模块需要它的错误报告函数及相应的宏.
• http_main.h——定义服务器初始化的APIs.模块不可能需要该文件.
• http_protocol.h——包含高级别的功能APIs,完成执行一系列的重要操作,包括所有正常的到客户的I/O请求和填充HTTP协议的各个字段以产生正确的相应头信息.它也导出超出http_request作用域的请求拦截处理.许多模块需要这个头文件——例如,内容产生器(除非你使用低级别的APIs)和认证模块.
• http_request.h——定义的主要API会在第6章中讨论,它导出了大多数请求处理拦截,子请求和内部跳转APIs.它不是被所有需要,只被一部分模块需要.
• http_vhost.h——包含API管理虚拟主机,它很少被模块需要,除了关心虚拟主机配置的模块.
• httpd.h——包含apache核心API,所有模块都需要这个文件(大概).定义了大量的系统常量,一些从本地编译参数中派生出来,和各种各样的APIs,例如HTTP状态码和HTTP请求方式.最重要的,它定义了在本章早些段落中的提到的核心对象.
其他我们将会遇到的重要API头文件:
• util_filter.h——过滤API,所有过滤模块都需要(第8章)
• ap_provider.h——provider API(第10章)
• mod_dbd.h——DBD框架(第10章和第11章)
其他可能对应用程序开发者感兴趣的API头文件:
• util_ldap.h——LDAP API
• util_script.h——最初用来支持CGI的脚本环境,但是也被其他使用CGI环境变量的模块(例如,mod_rewrite,mod_perl,mod_php)或者用CGI规则产生响应信息的模块(mod_asis)使用.
2.6 Apache配置基础
Apache的主要配置在服务器初始化和读取httpd.conf(和任何一个被包含的文件) 的时候确定.配置数据,包括从一个模块中继承的的资源(例如打开一个文件),这些被存在每一个模块的配置记录上.
每一个模块有两个配置记录,其中一个或者两个都是空(没有被使用):
• 每一服务器的配置被直接存在server_rec,因此一个虚拟主机一个实例.服务器的指令作用域被httpd.conf文件中的<VirtualHost>容器控制.但是其他的容器指令,例如, <Location>, <Directory>,和 <Files>会被忽略.
• 每个目录的配置被间接存储的,在处理请求的过程中通过request_rec对象被模块访问到.这个是和每一服务器的配置是相反的:它的作用域在容器指令定义范围内,例如<Location>, <Directory>, and <Files>.
实现一个配置指令,模块必须提供一个识别指令的函数,并且在初始化阶段设置一个配置集合的一个字段.在初始化之后,配置被设置完,不应该被改变.在特殊情况下,配置集合一般应该被对待为只读,在处理请求(或者连接)时候.在处理请求过程中改变配置数据是违反线程安全的(需要使用编程技巧,例如锁),并且加大产生其他bug的风险,最终导致系统的复杂性.Apache为每一个conn_rec和request_rec临时数据提供了一个独立配置集合.
第9章描述配置集合和数据
2.7 Apache的请求处理过程
…明天补上