+ -
当前位置:首页 → 问答吧 → 发几个服务器源代码供大家学习参看

发几个服务器源代码供大家学习参看

时间:2007-08-01

来源:互联网

对于我们开发web的程序员来说,没有web服务器基本都不知道自己能做什么,这样,连接服务器的工作原理,及服务器各种头标,响应码
的作用是很有必要的.这样你就知道很多机制,知道了原理,开发起来就明了的多了。

把.rar改成.tgz就可以了.(这个可是纯php写的web服务器啊.很有参考价值)
附件: 您所在的用户组无法下载或查看附件

作者: 蟋蟀   发布时间: 2007-08-01

...

作者: 蟋蟀   发布时间: 2007-08-01

昵称: 蟋蟀  时间: 2007-8-1 09:27
附件: 您所在的用户组无法下载或查看附件

作者: 蟋蟀   发布时间: 2007-08-01

...

作者: 蟋蟀   发布时间: 2007-08-01

...

作者: 蟋蟀   发布时间: 2007-08-01

...

作者: 蟋蟀   发布时间: 2007-08-01


顶蟋蟀

作者: 蟋蟀   发布时间: 2007-08-01

谢谢,这东西对于有意识提高自身水平的程序员还是很有帮助的.等一下在发一些好东西.

作者: fengyun   发布时间: 2007-08-01

http://mail-archives.apache.org/mod_mbox/ apache官方网站
看看为什么apache叫apache吧.

作者: 蟋蟀   发布时间: 2007-08-01

昵称: 蟋蟀  时间: 2007-8-1 10:17
附件: 您所在的用户组无法下载或查看附件

作者: 蟋蟀   发布时间: 2007-08-01

基本书太大了,压缩也上不来,看样你们是没机会看了.哈哈

作者: 蟋蟀   发布时间: 2007-08-01

APR(Apache portable Run-time libraries,Apache可移植运行库)的目的如其名称一样,主要为上层的应用程序提供一个可以跨越多操作系统平台使用的底层支持接口库。在早期的Apache版本中,应用程序本身必须能够处理各种具体操作系统平台的细节,并针对不同的平台调用不同的处理函数。随着Apache的进一步开发,Apache组织决定将这些通用的函数独立出来并发展成为一个新的项目。这样,APR的开发就从Apache中独立出来,Apache仅仅是使用APR而已。目前APR主要还是由Apache使用,不过由于APR的较好的移植性,因此一些需要进行移植的C程序也开始使用APR,开源项目比如Flood loader tester(http://httpd.apache.org/test/flood/,该项目用于服务器压力测试,不仅仅适用于Apache)、FreeSwitch(www.freeswitch.org),JXTA-C(http://jxta-c.jxta.org,C版本的JXTA点对点平台实现);商业的项目则包括Blogline(http://www.bloglines.com/,covalent(http://www.covalent.net)等等。
APR使得平台细节的处理进行下移。对于应用程序而言,它们根本就不需要考虑具体的平台,不管是Unix、Linux还是Window,应用程序执行的接口基本都是统一一致的。因此对于APR而言,可移植性和统一的上层接口是其考虑的一个重点。而APR最早的目的并不是如此,它最早只是希望将Apache中用到的所有代码合并为一个通用的代码库,然而这不是一个正确的策略,因此后来APR改变了其目标。有的时候使用公共代码并不是一件好事,比如如何将一个请求映射到线程或者进程是平台相关的,因此仅仅一个公共的代码库并不能完成这种区分。APR的目标则是希望安全合并所有的能够合并的代码而不需要牺牲性能。
APR的最早的一个目标就是为所有的平台(不是部分)提供一个公共的统一操作函数接口,这是一个非常了不起的目的,当然也是不现实的一个目标。我们不可能支持所有平台的所有特征,因此APR目前只能为大多数平台提供所有的APR特性支持,包括Win32、OS/2、BeOS、Darwin、Linux等等。为了能够实现这个目标,APR开发者必须为那些不能运行于所有平台的特性创建了一系列的特征宏(FEATURE MACROS)以在各个平台之间区分这些特征。这些特征宏定义非常简单,通常如下:
APR_HAS_FEATURE
如果某个平台具有这个特性,则该宏必须设置为true,比如Linux和window都具有内存映射文件,同时APR提供了内存映射文件的操作接口,因此在这两个平台上,APR_HAS_MMAP宏必须设置,同时ap_mmap_*函数应该将磁盘文件映射为内存并返回适当的状态码。如果你的操作系统并不支持内存映射,那么APR_HAS_MMAP必须设置为0,而且所有的ap_mmap_*函数也可以不需要定义。第二步就是对于那些在程序中使用了不支持的函数必须提出警告。
目前APR中支持的基本类型包括下面几种:
表3-1 APR中支持的基本类型
类型名称 文件夹名称 描述
atomic /srclib/apr/atomic 原子操作
dso /srclib/apr/dso 动态加载共享库
file io /srclib/apr/file_io 文件IO处理
mmap /srclib/apr/mmap 内存映射文件
locks /srclib/apr/locks 进程和线程互斥锁
memory /srclib/apr/memory 内存池操作
network_io /srclib/apr/network_io 网络IO处理
poll /srclib/apr/poll 轮询IO
table /srclib/apr/tables Apache数组(堆栈)和表格以及哈希表
process /srclib/apr/threadproc 进程和线程操作
user /srclib/apr/user 用户和用户组操作
time /srclib/apr/time 时间操作
string /srclib/apr/strings 字符串操作
password /srclib/apr/passwd 终端密码处理
misc /srclib/apr/misc 大杂烩,不属于其余类的任何apr类型都可以放在里面
shmem /srclib/apr/shmem 共享内存
random /srclib/apr/random 随机数生成库

每一个APR的实现我们都在后面会详细描述。
1.2 APR版本规则
由于Apache组织的目标是将APR独立出来形成单独的第三方库,因此对其而言稳定的API接口就成为一个非常重要的必须考虑的方面。不过由于APR需要不断的往前方展,因此API接口的变化又是必然的趋势,因此如何平衡稳定性和变化性是APR开发者面临的一个极需解决的问题。为此APR采用了严格的版本规则来实现这一点。用户只需要简单的判断APR版本号,就可以很容易确定当前版本的兼容性:向前兼容、向后兼容还是前后同时兼容。
1.2.1版本概述
APR中使用三个整数来记录APR版本号:MAJOR.MINOR.PATCH。MAJOR表示当前APR的主版本号,它的变化通常意味着APR的巨大的变化,比如体系结构的重新设计,API的重新设计等等,而且这种变化通常会导致APR版本的向前不兼容。MINOR称之为APR的次版本号,它通常只反映了一些较大的更改,比如APR的API的增加等等,但是这些更改并不影响与旧版本源代码和二进制代码之间的兼容性。PATCH通常称之为补丁版本,通常情况下如果只是对APR函数的修改而不影响API接口的话都会导致PATCH的变化。
目前为止APR的最高版本是1.2.2,最早遵循这种规则的版本号是0.9.0,不过在0.9.0之前,APR还推出了两个版本a8和a9。不过有一点需要注意的是,我们后面描述的版本规则并不适合1.0.0以前的版本。对于1.0.0以前的版本(0.x.y),APR提供的API是可以任意的改变而没有任何的限制,因此这些版本的变化不遵循后面描述的版本规则。从1.0.0以后的所有版本都遵循。切记。
除非主版本号发生变化,否则如果某个应用程序使用了低版本的APR,那么如果将该版本用高版本的APR替代,应用程序必须能够无错误的编译通过,通常我们称之为前向兼容行;反之很明显,如果应用程序中使用了高版本的APR,那么如果将该版本用低版本的APR替代,则未必能够编译通过,通常我们称之为后向不兼容。
APR的发展中力图总是保持与旧版本的源代码和二进制版本之间的兼容性。通过源代码兼容,应用程序就可以在使用新版本的APR进行编译的时候不会报错,这样应用程序就不需要为了适应新的APR而做出调整,从而保持应用开发的一致性和持续性。除非APR的主版本号发生变更。这种兼容性反之则不成立。如果一个应用程序使用较高的MINOR版本开发,那么很明显,如果将该版本替换为MINOR相对较低的版本进行编译,则成功的可能性应该不是很大。
除了源代码方面的兼容性,APR还希望能够保持二进制之间的兼容性。通过保持二进制兼容,应用程序可以直接使用高版本的APR库(或者是DLL,或者使so文件)替换低版本的库文件,而不需要做任何修改,就可以链接成功。与源代码兼容一样,二进制的兼容也是向前兼容,而不保证向后兼容。
下面的表格演示了APR的版本规则策略:
原始版本 新版本 兼容性 原因
2.2.3 2.2.4 前后兼容 PATCH版本号的变化保持前向和后向兼容
2.2.3 2.2.1 前后兼容 PATCH版本号的变化保持前向和后向兼容
2.2.3 2.3.1 向前兼容 次版本号的变化保持向前兼容,但并不保持向后兼容
2.2.3 2.1.7 不兼容 次版本号的降低不能保证向后兼容
2.2.3 3.0.0 不兼容 主版本号的变化不保证兼容性
2.2.3 1.4.7 不兼容 主版本号的变化不保证兼容性

1.2.2版本策略
为了控制APR接口的稳定,APR制定了严格的版本变化策略。
1.2.2.1PATCH版本策略
在前表中我们看到,PATCH的变化并不影响版本的源代码和二进制级别的兼容性,包括向前和向后兼容,因此,我们很容易看出,PATCH版本变化通常意味着对版本的修修补补,即BUG的修复。这些工作通常被局限于函数内部的修改,或者是API函数内部,或者是APR内部static函数的变化。任何对API的增加、修改、删除都是不允许的。
1.2.2.1次版本号策略
任何新函数,新变量以及新常量的引入以及任何现有函数的废除都将可能导致次版本号的变化:
1)、新函数的引入
An application coded against an older minor release will still have all of its functions available with their original signatures. Once an application begins to use a new function, however, they will be unable to work against older minor versions.
It is tempting to say that introducing new functions might create incompatibility across minor releases. If an application takes advantage of an API that was introduced in version 2.3 of a library, then it is not going to work against version 2.2. However, we have stated that an any application built against version 2.2 will continue to work for all 2.x releases. Thus, an application that states "requires 2.3 or later" is perfectly acceptable -- the user or administrator simply upgrades the installed library to 2.3. This is a safe operation and will not break any other application that was using the 2.2 library.
In other words, yes an incompatibility arises by mandating that a specific version needs to be installed. But in practice, this will not be a problem since upgrading to newer versions is always safe.
2)、新常量的引入
Similar to functions, all of the original (old) constants will be available to an application. An application can then choose to use new constants to pick up new semantics and features.
3)、函数替换
This gets a bit trickier. The original function must remain available at the link-level so that an application compiled against a minor version will continue to work with later minor versions. Further, if an application is designed to work with an earlier minor version, then we don't want to suddenly change the requirements for that application. This means that the headers cannot silently map an old function into a newer function, as that would turn an application, say, based on 1.2 into an application requiring the 1.4 or later release.
This means that functions cannot truly be replaced. The new, alternate function can be made available in the header and applications can choose to use it (and become dependent upon the minor release where the function appears).
It is possible to design a set of headers where a macro will always refer to the "latest" function available. Of course, if an application chooses to use this macro, then the resulting compiled-binary will be dependent upon whatever version it was compiled against. This strategy adds the new functionality for applications, yet retains the necessary source and binary compatibility for applications designed or built against previous minor releases.
Constants (enumerated values and preprocessor macros) are not allowed to change since an older application will still be using them. Similarly, function signatures at the link-level may not change, so that support for older, compiled applications is maintained.
4)、函数作废
随着APR的升级,APR中的一些API可能将作废,不再使用,但是这些API并不能从APR库中移除。因为一旦API被移除,向后兼容性将被破坏。因此我们能够做的仅仅是宣布其作废。
If you deprecate a function in APR, please mark it as such in the function documentation, using the doxygen "\deprecated" tag. Deprecated functions can only be removed in major releases.
A deprecated function should remain available through the original header. The function prototype should remain in the same header, or if moved to a "deprecated functions" header, then the alternate header should be included by the original header. This requirement is to ensure that source compatibility is retained.

Finally, if you are deprecating a function so that you can change the name of the function, please use the method described above under "Replacing functions", so that projects which use APR can retain binary compatibility.
Note that all deprecated functions will be removed at the next major version bump.
1.2.2.3主版本号策略
下面的任何一种变化都将可能导致主版本号的变化:
1)、常量的移除或者更改
2)、函数移除或者作为
3)、fold together macro-ized function replacements
1.2.3版本检查
由于APR严格的版本控制策略,使得应用程序在使用APR库之前必须能够检测使用的APR库的版本号。APR允许在编译以及使用APR的时候检测它的版本号。
1.2.3.1 编译时版本检查
Libraries should make their version number available as compile-time constants. For example:
#define FOO_MAJOR_VERSION 1
#define FOO_MINOR_VERSION 4
#define FOO_PATCH_VERSION 0
The above symbols are the minimum required for this specification.
An application that desires, at compile-time, to decide on whether and how to use a particular library feature needs to only check two values: the major and the minor version. Since, by definition, there are no API changes across patch versions, that symbol can be safely ignored. Note that any kind of a check for a minimum version will then pin that application to at least that version. The application's installation mechanism should then ensure that that minimal version has been installed (for example, using RPM dependency checks).
If the feature changes across minor versions are source compatible, but are (say) simply different choices of values to pass into the library, then an application can support a wider variety of installed libraries if it avoids compile-time checks.
1.2.3.2 执行时版本检查
A library meeting this specification should support a way for an application to determine the library's version at run-time. This will usually be emboded as a simple function which returns the MAJOR, MINOR, and PATCH triplet in some form.
Run-time checks are preferable in all cases. This type of check enables an application to run against a wider variety of minor releases of a library (the application is "less coupled" to a particular library release). Of course, if an application requires a function that was introduced in a later, minor release, then the application will require that, at least, that release is installed on the target system.
Run-time checks are particurly important if the application is trying to determine if the library has a particular bug that may need to be worked around, but has been fixed in a later release. If the bug is fixed in a patch release, then the only avenue for an application is to perform a runtime check. This is because an application cannot require a specific patch level of the library to be installed -- those libraries are perfectly forward and backwards compatible, and the administrator is free to choose any patch release, knowing that all applications will continue to function properly. If the bug was fixed in a minor release, then it is possible to use a compile-time check, but that would create a tighter coupling to the library.
1.2.3.3 版本API
与前面的版本规则定义一致,APR中定义了数据结构apr_version_t来描述版本规则:
typedef struct {
    int major;      /**< major number */
    int minor;      /**< minor number */
    int patch;      /**< patch number */
    int is_dev;     /**< is development (1 or 0) */
} apr_version_t;
major是当前APR版本的主版本号,minor则是次版本号,patch对应的则是APR的补丁号。es_dev则描述了当前APR库的状态:开发版还是发行版,分别对应1和0。
一旦定义了apr_version_t结构,APR就将使用它作为基本的版本控制单位。APR中提供了函数apr_version和apr_version_string分别设置和返回APR的版本。
APR_DECLARE(void) apr_version(apr_version_t *pvsn)
{
    pvsn->major = APR_MAJOR_VERSION;
    pvsn->minor = APR_MINOR_VERSION;
    pvsn->patch = APR_PATCH_VERSION;
#ifdef APR_IS_DEV_VERSION
    pvsn->is_dev = 1;
#else
    pvsn->is_dev = 0;
#endif
}
apr_version函数仅仅就是设置apr_version_t结构中的各个成员。对于每一个APR版本,APR都会将当前的版本号分别用三个常量定义在version.h中,比如,如果版本号是2.2.0,则常量定义应该如下:
#define APR_MAJOR_VERSION       2
#define APR_MINOR_VERSION       2
#define APR_PATCH_VERSION       0
apr_version_string函数仅仅是返回APR_VERSION_STRING宏,该宏定义为:
#define APR_VERSION_STRING \
     APR_STRINGIFY(APR_MAJOR_VERSION) "." \
     APR_STRINGIFY(APR_MINOR_VERSION) "." \
     APR_STRINGIFY(APR_PATCH_VERSION) \
     APR_IS_DEV_STRING
APR_STRINGIFY用于将给定的数值转换为字符串,因此APR_VERSION_STRING宏就是无非将APR_MAJOR_VERSION,APR_MINOR_VERSION,APR_PATCH_VERSION分别转换为字符串,再用"."连接起来,最后的形式应该为“2.2.0”。
尽管一般情况下,APR的版本号是“x.x.x”的格式,不过在Window的资源文件.rc中通常是“x,x,x”格式,因此APR中也提供了APR_VERSION_STRING_CSV来提供这种格式的版本号:
#define APR_VERSION_STRING_CSV APR_MAJOR_VERSION ##, \
                             ##APR_MINOR_VERSION ##, \
                             ##APR_PATCH_VERSION
##宏用于将变量与特定字符进行连接形成新的字符串。在后面的部分,我们会不断看到它的用法。
在一些情况下,应用程序需要使用的APR版本达到一定的版本号,为此,APR中提供了APR_VERSION_AT_LEAST宏用以检测给定的APR库是否达到给定的版本要求:
#define APR_VERSION_AT_LEAST(major,minor,patch)                    \
(((major) < APR_MAJOR_VERSION)                                     \
|| ((major) == APR_MAJOR_VERSION && (minor) < APR_MINOR_VERSION) \
|| ((major) == APR_MAJOR_VERSION && (minor) == APR_MINOR_VERSION && (patch) <= APR_PATCH_VERSION))
如果我们希望当前使用的APR库的版本不的低于”1.2.0”,那么我们就使用使用APR_VERSION_AT_LEAST(1,2,0)对当前的APR库版本进行检查。

作者: 蟋蟀   发布时间: 2007-08-01

1.3 APR构建
1.3.1获取APR
编译APR的第一个步骤就是获取APR开发包。通常情况下,你可以到APR的官方网站http://apr.apache.org/download.cgi去下载。
一般情况下,APR开发包很容易理解为仅仅是一个开发包,不过事实上并不是。目前,完整的APR实际上包含了三个开发包:apr、apr-util以及apr-iconv,每一个开发包分别独立开发,并拥有自己的版本。
apr开发包位于目录${APR}\apr下,其中包含了一些通用的开发组件,包括mmap,文件等等,前面已经描述过。
apr-util开发包位于目录${APR}\apr-util下,该目录中也是包含了一些常用的开发组件。这些组件与apr目录下的相比,它们与apache的关系更加密切一些。比如存储段和存储段组,加密等等,具体的各个组件的含义如下表所示:
组件名称 文件夹名称 描述
buckets /srclib/apr-util/buckets 存储段和存储段组
crypto /srclib/apr-util/crypto 加密和解密
hooks /srclib/apr-util/hooks apache挂钩
dbd /srclib/apr-util/dbd 数据库连接管理
dbm /srclib/apr-util/dbm   
ldap /srclib/apr-util/ldap 轻量级目录访问协议
strmatch /srclib/apr-util/strmatch 字符串匹配,包括普通字符串匹配以及正则表达式匹配,正则表达式匹配中使用prec库
uri /srclib/apr-util/uri uri操作例程
xml /srclib/apr-util/xml xml支持例程,其中使用expat作为xml解析器
xlate /srclib/apr-util/xlate i18n 转换库
encoding /srclib/apr-util/encoding 编码转换库,其中实现了各种编码之间的转换
misc /srclib/apr-util/misc 大杂烩

apr-util的当前版本为1.2.2,最早的版本为0.9.1。
apr-iconv包中的文件主要用于实现iconv编码。目前的大部分编码转换过程都是与本地编码相关的。在进行转换之前必须能够正确地设置本地编码。因此如果两个非本地编码A和B需要转换,则转换过程大致为A->Local以及Local->B或者B->Local以及Local->A。
XPG2 标准中另外定义了一组全新的函式接口ICONV,该接口是一种更广义的字集转换系统。也是一个与本地编码无关的字集转换系统。APR中也支持这种字集转换系统,为此它提供了完整的实现。Apr-iconv正是为此目的而产生。
apr-iconv的当前版本为1.1.1,最早的版本从0.9.2开始。
1.3.2APR的目录组织
http://www.apache.org/上下载apr-1.1.1.tar.gz到本地解压后,可以发现APR的目录结构很清晰。
1) 所有的头文件都放在$(APR)/include目录中;
2) 所有功能接口的实现都放在各自的独立目录下,如threadproc、mmap等目录中,而这些目录的子目录中包含实际的实现代码。Apache中划分子目录的根据就是编译代码所在的平台,目前APR能够支持Unix、BeOS、Windows以及OS2四种平台,因此正常情况下,每一个目录中会包含这四个子目录。比如文件I/O的目录就如下所示:
apr
|
   -> file_io
          |
           -> unix            The Unix and common base code
          |
           -> win32           The Windows code
          |
           -> os2             The OS/2 code
在上面的四种子目录中,unix是一个比较特殊的目录。由于Unix的种类很多,比如FreeBSD,Linux等等,原则上应该都为这些平台分别建立各自的子目录,然后分别实现代码。不过所有的Unix都大同小异,如果为了稍许的差异就“大动干戈”,不太划算,因此APR中将所有的Unix的平台的操作合并到一起,而这些平台这些的差异就用前面的预定义宏来区别。目前的unix目录中以POSIX为主,同时兼顾System V。上面的文件I/O操作中明显的缺少了BeOS平台的实现,那是因为BeOS的实现合并到Unix目录中去了。
另外两个特殊的目录就是include和test目录了。include目录中包含了所有的外部使用所需要的头文件。其中APR.h和apr_private.h是两个特殊的文件,如果要使用APR,必须包含apr.h头文件。但在原文件中并看不到这两个文件。事实上,这两个文件都是自动生成的。由于windows和netware平台下并不使用autoconf,因此APR在windows和netware平台下的行为与其余所有平台都不相同。在UNIX上,apr_private.h(APR私有文件,仅仅APR内部使用)和apr.h(APR公共文件,可以其余的文件使用)实际上在代码中并不存在,它们都是由autoconf从acconfig.h和apr.h.in中自动生成的。而在Window中,这两个文件都由apr_private.hw(apr_private.hwn)和apr.hw(apr_private.hwn)中自动生成。
test则是测试程序的目录。每一个APR类型在使用之前都必须经过测试。事实上对于APR的使用者而言,这个目录还有另外的一个好处,就是快速掌握APR类型的使用。每一个测试例子都给出了具体类型的使用方法。
3) 此外就是相关平台构建工具文件如Makefile.in,configure.in等等。
1.3.3APR构建
在Window和Unix上编译APR的方法不太相同,我们分开来描述。
1.3.3.1 Unix上编译
We've attempted to ensure that compiling apr, apr-iconv and apr-util distribution tarballs requires nothing more than what comes installed by default on various UNIX platforms.
All you should have to do is this:
   ./configure
   make
   make install
As of this writing, APR is not quite ready to be installed as a system-wide shared library; it currently works best when tied directly to the application using it.
Note that if you are compiling directly from the SVN repository, you'll need GNU autoconf and GNU libtool installed, and you'll need to run ./buildconf before running the commands listed above.
1.3.3.2 Window平台上编译
The apr-util/aprutil.dsw workspace builds the .dsp projects of the Apache server listed with dependent projects preceeding their dependencies:
apr-util\aprutil.dsp
apr-util\libaprutil.dsp
apr-util\uri\gen_uri_delims.dsp
apr-util\xml\expat\lib\xml.dsp
apr-iconv\apriconv.dsp
apr-iconv\libapriconv.dsp
apr\apr.dsp
apr\libapr.dsp
The libXXX projects create .dll targets, dynamic shared libraries. Their non-libXXX counterparts create static .lib targets.
To compile code for the libraries, the consuming compiliation must include the apr/include and apr-util/include directories in their include file search paths. To target the static .lib versions of the library, the consuming compiliation must define the macros APR_DECLARE_STATIC and APU_DECLARE_STATIC. This prevents the apr and apr-util symbols from being tagged as __declspec(dllimport), eliminating compiliation warnings and speeding up execution.

在Window平台上要成功编译apr、apr-iconv以及apr-util,必须具备一下的几个条件:
1)、可用的微软编译器:比如微软的Visual C++ 5.0或者更高的版本,比如Visual C++6.0,Microsoft Visual Studio.NET 2002,Microsoft Visual Studio.NET 2003(必须具备Visual C++ .NET编译器)。
对于Visual C++ 5.0的用户,为了能够使用一些APR中的新特性,你必须更新Windows平台开发包(Windows Platform SDK)。对于Visual C++ 6.0则没有这些多余的事情,因为这些SDK随Visual C++6.0一起发布了。如果没有这些新的SDK,使用MSVC++5.0编译的时候,编译中会出现大量新特性不支持的警告,甚至完全编译失败。至于具体的SDK,你可以到Window的网站上去下载。
目前最近的APR版本是2.2.0,不过你如果使用老一些的APR,比如1.1.1以前,那么你还需要awk。你可以到http://cm.bell-labs.com/cm/cs/who/bwk/awk95.exe去下载二进制可执行文件。不过从1.1.1以后的版本就可以省去这个麻烦了。
2)、正确的目录布局
除了必要的编译工具之外,APR开发包还必须具备正确地目录布局。apr,apr-util以及apr-iconv必须同时具备,并且它们必须位于同一目录之下,比如:
C:\work\apr\
C:\work\apr-iconv\
C:\work\apr-util\
对于发行版本,直接将发行的文件包解压到指定目录下即可;而对于开发版本,你必须能够从subversion中自行检出,Window平台下,APR推荐的SVN是TortoiseSVN。
万事具备,只欠东风。现在你可以编译APR了。你可以选择两种方式,或者是命令行编译,或者是使用IDE编译。
■      命令行方式编译
使用命令行进行编译的第一步就是修改vcvars32.bat,通常情况下该文件位于C:\Program Files\Microsoft Visual Studio\VC98\Bin目录下,其中C:\Program Files\Microsoft Visual Studio\是VC的安装目录,它根据安装目录的不同会不同。
"C:\Program Files\DevStudio\VC\Bin\vcvars32.bat"
If necessary, you will also need to prepare the Platform SDK environment:
如果有必要,你还必须准备Platform SDK相关的环境变量,这个通常修改setenv.bat文件就可以实现:
"C:\Program Files\Platform SDK\setenv.bat"
一旦设置完毕,你首先必须切换到apr-util目录下,然后简单的执行下面的指令就可以编译APR了。
msdev aprutil.dsw /MAKE \
    apriconv - Win32 Release" \
    apr - Win32 Release" \
    libapr - Win32 Release" \
    gen_uri_delims - Win32 Release" \
    xml - Win32 Release" \
    "aprutil - Win32 Release" \

msdev aprutil.dsw /MAKE \
    libapr - Win32 Release" \
    libapriconv - Win32 Release" \
    gen_uri_delims - Win32 Release" \
    xml - Win32 Release" \
    libaprutil - Win32 Release" \
这两个命令都可以编译APR,不过它们的区别就是后一个编译结果是动态链接库.dll,而前者则是静态连接库.lib。不过它们编译的都是发行版,如果你需要编译调试版本,只需要简单的将命令中的”Release”替换为”Debug”即可,这样,你就可以方便的进行调试了。
For Visual Studio C++ 5.0 command line users: Only the .dsp files are maintained within SVN. Win32 .mak files are NOT maintained in SVN, due to the tremendous waste of reviewer's time. Therefore, you cannot rely on the NMAKE commands above to build revised .dsp project files unless you then export all .mak files yourself from the project.
■      IDE方式编译
与命令行编译相比,使用IDE编译更简单。事实上,在使用的时候我更倾向于使用IDE进行编译。:)。不过如果你是那种什么都得挖到底的人,前面的命令行编译你也可以试试。
IDE编译,你需要的仅仅是一个dsw工作区aprutil.dsw,它位于apr-util目录下,该工作区中包含了完整编译整个APR所需要的所有的.dsp项目文件,以及各个dsp文件之间的依赖关系,以确保它们之间的正确的编译顺序。
打开aprutil.dsw,整个工作区如下图所示:

从上图中可以看出,apr-util.dsw工作区中包含了十个dsp工作项目。Apr、apriconv以及aprutil分别对应静态编译库,而libXXX则对应的是动态编译库。默认情况下,编译的是apr项目,不过你可以通过project->Set Active Project选择你需要编译的实际项目:

作者: 蟋蟀   发布时间: 2007-08-01

我们首先make install一下,比如我们在Makefile中指定prefix=$(APR)/dist,则make install后,在$(APR)/dist下会发现4个子目录,分别为bin、lib、include和build,其中我们感兴趣的只有include和lib。下面是一个APR app的例子project。
该工程的目录组织如下:
$(apr_path)
dist
    - lib
    - include
- examples
    - apr_app
      - Make.properties
      - Makefile
      - apr_app.c
我们的Make.properties文件内容如下:
#
# The APR app demo
#
CC              = gcc -Wall
BASEDIR         = $(HOME)/apr-1.1.1/examples/apr_app
APRDIR          = $(HOME)/apr-1.1.1
APRVER          = 1
APRINCL         = $(APRDIR)/dist/include/apr-$(APRVER)
APRLIB          = $(APRDIR)/dist/lib
DEFS            = -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -D_DEBUG_
LIBS            = -L$(APRLIB) -lapr-$(APRVER) \
                  -lpthread -lxnet -lposix4 -ldl -lkstat -lnsl -lkvm -lz -lelf -lm -lsocket �Cladm
INCL            = -I$(APRINCL)
CFLAGS          = $(DEFS) $(INCL)
Makefile文件内容如下:
include Make.properties
TARGET  = apr_app
OBJS    = apr_app.o
all: $(TARGET)
$(TARGET): $(OBJS)
        $(CC) ${CFLAGS} -o $@ $(OBJS) ${LIBS}
clean:
        rm -f core $(TARGET) $(OBJS)
而apr_app.c文件采用的是$(apr_path)/test目录下的proc_child.c文件。编译运行一切OK。
1.5 APR的可移植性
正如前面所描述,APR的目前的首要目标就是设计为一个跨平台的通用库,因此在APR的整个设计过程中无不体现了可移植的思想,APR附带一个简短的设计文档,文字言简意赅,其中很多的移植设计思想都值得我们所借鉴,主要从四个方面谈。
1.5.1APR类型
为了支持可移植性,APR中的一个策略就是尽量使用APR自定义的类型来代替平台相关类型。这样的好处很多,比如便于代码移植,避免数据间进行不必要的类型转换(如果你不使用APR自定义的数据类型,你在使用某些APR提供的接口时,就需要进行一些参数的类型转换);自定义数据类型的名字更加具有自描述性,提高代码可读性。APR提供的基本自定义数据类型包括apr_byte_t,apr_int16_t,apr_uint16_t,apr_size_t等等。通常情况下这些类型都定义在apr.h中,不过你找遍整个APR包也不会找到apr.h这个文件,不过include目录下倒是存在类似于apr.h的apr.h.in和apr.hw,这两个文件是生成apr.h的模版,在apr.h.in中通用APR类型定义如下:
typedef unsigned char              apr_byte_t;
typedef @short_value@           apr_int16_t;
typedef unsigned @short_value@       apr_uint16_t;
typedef @int_value@              apr_int32_t;
typedef unsigned @int_value@           apr_uint32_t;
typedef @long_value@            apr_int64_t;
typedef unsigned @long_value@         apr_uint64_t;
typedef @size_t_value@            apr_size_t;
typedef @ssize_t_value@         apr_ssize_t;
typedef @off_t_value@            apr_off_t;
typedef @socklen_t_value@              apr_socklen_t;
@xxx@变量的值是可变的,不同的平台其值可能不一样。其值由configure配置过程自动生成,configue脚本中设置@xxx@变量的部分大致如下:
AC_CHECK_SIZEOF(char, 1)
AC_CHECK_SIZEOF(short, 2)
AC_CHECK_SIZEOF(int, 4)
AC_CHECK_SIZEOF(long, 4)
AC_CHECK_SIZEOF(long long, 8)

if test "$ac_cv_sizeof_short" = "2"; then
    short_value=short
fi
if test "$ac_cv_sizeof_int" = "4"; then
    int_value=int
fi
if test "$ac_cv_sizeof_int" = "8"; then
    int64_value="int"
    long_value=int
elif test "$ac_cv_sizeof_long" = "8"; then
    int64_value="long"
    long_value=long
elif test "$ac_cv_sizeof_long_long" = "8"; then
    int64_value="long long"
    long_value="long long"
elif test "$ac_cv_sizeof_longlong" = "8"; then
    int64_value="__int64"
    long_value="__int64"
else
    AC_ERROR([could not detect a 64-bit integer type])
fi
if test "$ac_cv_type_size_t" = "yes"; then
    size_t_value="size_t"
else
    size_t_value="apr_int32_t"
fi
if test "$ac_cv_type_ssize_t" = "yes"; then
    ssize_t_value="ssize_t"
else
    ssize_t_value="apr_int32_t"
fi
.. ..
Configure的具体的细节并不是本书描述的细节,如果你想了解更多细节,你可以去阅读GNU的AutoConf、AutoMake等使用手册。
不同的操作系统中各个变量的值如下表所示:
变量类型 硬件平台
I686 I386  alpha IA64 M68k MIPS Sparc Spar64
apr_byte_t 1 1 1 1 1 1 1 1
apr_int16_t 2 2 2 2 2 2 2 2
apr_uint16_t 2 2 2 2 2 2 2 2
apr_int32_t 4 4 4 4 4 4 4 4
apr_uint32_t 4 4 4 4 4 4 4 4
apr_int64_t 4 4 8 8 4 4 4 4
apr_uint64_t 4 4 8 8 4 4 4 4

不过不同的操作系统中,定义各不相同,在Red Hat 9.0 Linux中,生成的定义如下:
typedef short                     apr_int16_t;        //16位整数
typedef unsigned short         apr_uint16_t;             //16位无符号整数
typedef int                         apr_int32_t;        //32位整数
typedef unsigned int              apr_uint32_t;             //32位无符号整数
typedef long long                            apr_int64_t;        //64位整数
typedef unsigned long long            apr_uint64_t;             //64位无符号整数

typedef size_t                          apr_size_t;          //
typedef ssize_t                        apr_ssize_t;
typedef off64_t                        apr_off_t;
typedef socklen_t                           apr_socklen_t;     //套接字长度
通用数据类型的另外一个定义之处就是文件apr_portable.h中,APR中提供了通用的数据类型以及对应的操作系统依赖类型如下表:
通用类型 含义 Win32类型 BEOS类型 UNIX
apr_os_file_t 文件类型 HANDLE int Int
apr_os_dir_t 目录类型 HANDLE dir DIR
apr_os_sock_t 套接字类型 SOCKET int int
apr_os_proc_mutex_t 进程互斥锁 HANDLE apr_os_proc_mutex_t pthread_mutex_t
apr_os_thread_t 线程类型 HANDLE thread_id pthread_t
apr_os_proc_t 进程类型 HANDLE thread_id pid_t
apr_os_threadkey_t 线程key类型 DWORD int pthread_key_t
apr_os_imp_time_t   FILETIME struct timeval struct timeval
apr_os_exp_time_t   SYSTEMTIME struct tm tm
apr_os_dso_handle_t DSO加载 HANDLE image_id void*
apr_os_shm_t 共享内存 HANDLE void* void*

一旦定义了这些通用的数据类型,APR不再使用系统类型,而是上述的APR类型。不过由于系统底层仍然使用系统类型,因此在使用通用类型的时候一项必须的工作就是用实际的类型来真正替代通用类型,比如apr_os_file_t,如果是Win32平台,则必须转换为HANDLE。对于上面表格的每一个通用数据类型,Apache都提供两个函数支持这种转换:
APR_DECLARE(apr_status_t) apr_os_XXX_get(…);
APR_DECLARE(apr_status_t) apr_os_XXX_put(…);
get函数用于将通用的数据类型转换为特定操作系统类型;而put函数则是将特定操作系统类型转换为通用数据类型。比如对于file类型,则对应的函数为:
APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,
apr_file_t *file);
APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file,
                                          apr_os_file_t *thefile,
                                          apr_int32_t flags, apr_pool_t *cont);
前者将通用的文件类型apr_os_file_t转换为特定操作系统类型apr_file_t,后者则是将apr_file_t转换为apr_os_file_t。
在后面的分析中我们可以看到,对于每一个组件类型,比如apr_file_t中都会包含系统定义类型,APR类型都是围绕系统类型扩充起来的,比如apr_file_t,在Unix中为:
struct apr_file_t
{
    int filedes;              //UNIX下的实际的文件系统类型
    … …
}
而在Window中则是:
struct apr_file_t
{
    HANDLE filedes;     //Window下的实际的文件系统类型
    ……
}
因此apr_os_file_get函数无非就是返回结构中的文件系统类型,而apr_os_file_put函数则无非就是根据系统文件类型创建apr_file_t类型。
类似的例子还有apr_os_thread_get和apr_os_thread_put等等。
1.5.2函数
APR中函数可以分为两大类:内部函数和外部接口函数。顾名思义,内部函数仅限于APR内部使用,外部无法调用;而外部结构函数则作为API结构,由外部程序调用。
1.5.2.1内部函数
APR中所有的内部函数都以static进行修饰。通常理解static只是指静态存储的概念,事实上在里面static包含了两方面的含义。
      1)、在固定地址上的分配,这意味着变量是在一个特殊的静态区域上创建的,而不是每次函数调用的时候在堆栈上动态创建的,这是static的静态存储的概念。
2)、另一方面,static能够控制变量和函数对于连接器的可见性。一个static变量或者函数,对于特定的编译单元来说总是本地范围的,这个范围在C语言中通常是指当前文件,超过这个范围的文件或者函数是不可以看到static变量和函数的,因此编译器也无法访问到这些变量和函数,它们对编译器是不可见的。因此内部函数是不允许被直接调用的,任何直接调用都导致“尚未定义”的错误。不过潜在的好处就是,内部函数的修改不影响API接口。
static的这两种用法APR中都存在,但是第二种用法较多。
1.5.2.2外部API函数
对于APR用户而言,它们能够调用的只能是APR提供的API。要识别APR中提供的API非常的简单,如果函数是外部API,那么它的返回值总是用APR_DECLARE或者APR_DECLARE_NONSTD进行包装,比如:
APR_DECLARE(apr_hash_t *) apr_hash_make(apr_pool_t *pool);
APR_DECLARE(int) apr_fnmatch_test(const char *pattern);
APR_DECLARE和APR_DECLARE_NONSTD是两个宏定义,它们在apr.h中定义如下:
#define APR_DECLARE(type)            type
#define APR_DECLARE_NONSTD(type)     type
APR_DECLARE和APR_DECLARE_NONSTD到底是什么意思呢?为什么要将返回类型封装为宏呢?在apr.h中有这样的解释:
/**
* The public APR functions are declared with APR_DECLARE(), so they may
* use the most appropriate calling convention. Public APR functions with
* variable arguments must use APR_DECLARE_NONSTD().
*
* @remark Both the declaration and implementations must use the same macro.
* @example
*/
/** APR_DECLARE(rettype) apr_func(args)
* @see APR_DECLARE_NONSTD @see APR_DECLARE_DATA
* @remark Note that when APR compiles the library itself, it passes the
* symbol -DAPR_DECLARE_EXPORT to the compiler on some platforms (e.g. Win32)
* to export public symbols from the dynamic library build.\n
* The user must define the APR_DECLARE_STATIC when compiling to target
* the static APR library on some platforms (e.g. Win32.) The public symbols
* are neither exported nor imported when APR_DECLARE_STATIC is defined.\n
* By default, compiling an application and including the APR public
* headers, without defining APR_DECLARE_STATIC, will prepare the code to be
* linked to the dynamic library.
*/
#define APR_DECLARE(type)            type
/**
* The public APR functions using variable arguments are declared with
* APR_DECLARE_NONSTD(), as they must follow the C language calling convention.
* @see APR_DECLARE @see APR_DECLARE_DATA
* @remark Both the declaration and implementations must use the same macro.
* @example
*/
/** APR_DECLARE_NONSTD(rettype) apr_func(args, ...);
*/
#define APR_DECLARE_NONSTD(type)     type
从上面的解释中我们可以看出“APR的固定个数参数公共函数的声明形式APR_DECLARE(rettype) apr_func(args);而非固定个数参数的公共函数的声明形式为APR_DECLARE_NONSTD(rettype) apr_func(args, ...);”。
在apr.h文件中解释了这么做就是为了在不同平台上编译时使用“the most appropriate calling convention”,这里的“calling convention”是一术语,翻译过来叫“调用约定”。 我们知道函数调用是通过栈操作来完成的,在栈操作过程中需要函数的调用者和被调用者在下面的两个问题上做出协调,达成协议:
a) 当参数个数多于一个时,按照什么顺序把参数压入堆栈
b) 函数调用后,由谁来把堆栈恢复原来状态
c) 产生函数修饰名的方法
在像C/C++这样的中、高级语言中,使用“调用约定”来说明这两个问题。常见的调用约定有:__stdcall、__cdecl、__fastcall、thiscall和naked call。
__stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈。
__cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意:对于可变参数的成员函数,始终使用__cdecl的转换方式。
__fastcall调用约定规定通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。
thiscall仅仅应用于"C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
naked call采用上述调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。
另外不同的调用约定对于函数内部修饰名处理的方法也不一样。所谓修饰名是C或者C++函数在内部编译和链接的时候产生的唯一的标识名称。
对于C语言而言,__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,格式为_functionname@number,例如 :function(int a, int b),其修饰名为:_function@8
__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。
__fastcall调用约定在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,格式为@functionname@number。
如果是C++,不同调用约定处理要稍微复杂一点。由于Apache是基于C语言开发,因此本处不再描述。
1.5.2.3内存池参数
关于函数的最后一个问题就是它的参数,如果函数内部需要分配空间,那么你就可以看到参数的参数中肯定包含一个apr_pool_t参数,比如:
APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m,
                                         const char *filename,
                                         apr_pool_t *pool);
由于Apache服务器所具有的一些特性,APR中并没有使用普通的malloc/free内存管理策略,而是使用了自行设计的内存池管理策略。APR中所有的需要的内存都不再直接使用malloc分配,然后首先分配一块足够大的内存块,然后每次需要的时候再从中获取;当内存不再使用的时候也不是直接调用free,而是直接归还给内存池。只有当内存池本身被释放的时候,这些内存才真正的被free给操作系统。Apache中使用apr_pool_t描述一个内存池,因此毫无疑问,由于这种特殊的内存分配策略,对于任何一个函数,如果你需要使用内存,那么你就应该指定内存所源自的内存池。这就是为什么大部分函数参数中都具有apr_pool_t的原因。关于内存池的详细细节,我们在第二章详细讨论。

作者: 蟋蟀   发布时间: 2007-08-01

1.5.3错误处理
大型的系统程序的错误处理是十分重要的,APR作为一个通用库接口集合详细的说明了使用APR时如何进行错误处理。
1.5.3.1 错误码定义
错误处理的第一步就是定义返回码,包括“错误码和状态码分类”。APR的函数大部分都返回int类型作为返回码的,不过为了更明确易懂,APR在apr_errno.h中使用typedef int apr_status_t将其进行了重新定义。它在一起定义的还有apr所用的所有错误码和状态码。如果一个APR函数绝对的不可能出错,那么此时就允许不返回ap_status_t错误码,只需要返回void类型即可。不过APR中大部分的函数都是返回apr_status_t值。
APR中的返回码的定义并不是随意的,没有规则的。相反,APR给出了定义返回码的严格的规定。APR中根据返回信息的相似性将它们分为七大类,分别定义如下所示:
#define APR_OS_ERRSPACE_SIZE    50000
#define APR_SUCCESS                            0
#define APR_OS_START_ERROR              20000
#define APR_OS_START_STATUS             (APR_OS_START_ERROR + APR_OS_ERRSPACE_SIZE)
#define APR_OS_START_USERERR    (APR_OS_START_STATUS + APR_OS_ERRSPACE_SIZE)
#define APR_OS_START_CANONERR (APR_OS_START_USERERR \
                                         + (APR_OS_ERRSPACE_SIZE * 10))
#define APR_OS_START_EAIERR              (APR_OS_START_CANONERR + APR_OS_ERRSPACE_SIZE)
#define APR_OS_START_SYSERR             (APR_OS_START_EAIERR + APR_OS_ERRSPACE_SIZE)
正常情况下,函数返回APR_SUCCESS作为成功标志。否则根据情况返回上述类别中的任一种。每一大类的返回信息又可以具体细分为实际的值。在Apache中,每一类返回信息所允许的数目由APR_OS_ERRSPACE_SIZE决定,目前为50000,APR_OS_CANONERR为500000。另一方面,APR_OS_START_ERROR、APR_OS_START_STATUS、APR_OS_START_USEERR、APR_OS_START_CANONERR、APR_OS_START_EAIERR和APR_OS_START_SYSERR,它们每个都拥有自己独自的偏移量,具体偏移量的值以及含义如下表描述:
错误名称 含义
0 每个平台都有0,但是都没有实际的定义,0又的确是一个errno value的offset,但是它是“匿名的”,它不像EEXIST那样有着可以“自描述”的名字。
APR_OS_START_ERROR 该定义是平台相关的,不同平台的值可能不同,它定义了APR中所允许的错误码的起始偏移量,即20000,这意味着所有的错误码值不能低于20000。至于错误码,它可以是导致APR函数失败的任何原因。在这个范围内定义的所有错误码形式都必须是APR_E*格式,比如APR_ENOSTAT、APR_ENOSOCKET、APR_ENOPOOL。该类别中允许定义最多50000种错误码。
APR_OS_START_STATUS 该定义也是平台相关的,它定义了APR中所允许的返回状态值的起始偏移量,即APR_OS_START_ERROR + APR_OS_ERRSPACE_SIZE,即70000开始。不过需要注意的是,状态值并不能表示失败还是成功。如果要表示返回成功,必须使用APR_SUCCESS返回。在这个范围的状态码形式都必须是APR_*格式,比如APR_DETACH、APR_INCHILD。
APR_OS_START_USERERR
APR_OS_START_USEERR 该定义也是平台相关的。当用户使用APR库的时候,如果它希望定义APR返回码之外的其余的自定义返回码,那么这些返回码必须从APR_OS_START_USEERR开始,即APR_OS_START_STATUS + APR_OS_ERRSPACE_SIZE,即从120000开始。
APR_OS_START_CANONERR APR_OS_START_CANONERR is where APR versions of errno values are defined on systems which don't have the corresponding errno.对于这类返回码的定义通常如下:
#ifdef EEXIST
#define APR_EEXIST EEXIST
#else
#define APR_EEXIST         (APR_OS_START_CANONERR + 2)
#endif
APR_OS_START_EAIERR 在使用socket编程的时候如果调用getaddrinfo()函数,该函数会返回一系列的以EAI_*开始的错误码,比如EAI_AGAIN,EAI_FAIL等等。这些不规则的错误码最终都要转换为APR中对应的返回码。这些转换码从APR_OS_START_EAIERR开始,最多允许50000个(当然事实上肯定没有这么多)。
APR_OS_START_SYSERR 由于APR必须保持跨平台的特性,因此不同的操作系统平台肯定有自己所独有的一些返回码,这些返回码不具有移植性,是与平台相关的。尽管如此,他们也必须转换为APR内部返回码。APR_OS_START_SYSERR指定了定义这些不具有移植性返回码的起始偏移,为720000,容量为50000。

各返回信息起始便移量及其区别可以用下图描述:
从上表可以看出,所有的APR定义的返回码都是从APR_OS_START_ERROR开始,那么0到APR_OS_START_ERROR之间将近20000个空位岂不是浪费了?事实上这部分空间并没有浪费。我们后面所描述的”native error”会占用这部分的空隙。
Apache中的返回码如下表所示:
错误名称 含义 值
APR_ENOSTAT APR无法对一个文件执行stat操作 20001
APR_ENOPOOL APR没有提供内存池来执行内存分配操作 20002
APR_EBADDATE APR给出了一个无效的日期 20003
APR_EINVALSOCK APR给出了一个无效的socket 20004
APR_ENOPROC APR没有给定一个进程的结构 20005
APR_ENOTIME APR没有给定一个时间结构 20006
APR_ENODIR APR没有给定一个目录结构 20007
APR_ENOLOCK APR没有给定一个互斥锁结构 20008
APR_ENOPOLL APR没有给定一个Poll结构 20009
APR_ENOSOCKET APR没有给定一个socket 20010
APR_ENOTHREAD APR没有给定一个线程结构 20011
APR_ENOTHDKEY APR没有给定一个线程Key结构 20012
APR_ENOSHMAVAIL APR中没有更多的可用共享内存 20013
APR_EDSOOPEN APR中无法打开一个DSO对象 20014
APR_EGENERAL APR中的通常的错误 20015
APR_EBADIP 描述的IP地址错误 20016
APR_EBADMASK 描述的IP地址掩码错误 20017
APR_ESYMNOTFOUND 无法查找到请求symbo 20018
APR_INCHILD 程序正在执行子进程 70001
APR_INPARENT 程序正在执行父进程 70002
APR_DETACH 线程从主线程中被分离出来 70003
APR_NOTDETACH 线程尚未从主线程中分离出来 70004
APR_CHILD_DONE 子进程已经执行完毕 70005
APR_CHILD_NOTDONE 子进程尚未执行完毕 70006
APR_TIMEUP 执行操作超时 70007
APR_INCOMPLETE The operation was incomplete although some processing was performed and the results are partially valid 70008
APR_BADCH Getopt函数查找到一个不在选项字符串中的选项 70012
APR_BADARG Getopt发现一个选项缺少参数,而在选项字符串中该选项必须指定 70013
APR_EOF APR已经到达文件的末尾 70014
APR_NOTFOUND APR在poll结构中无法发现socket 70015
APR_ANONYMOUS APR正在使用匿名的共享内存 70019
APR_FILEBASED APR正在使用文件名作为共享内存的key 70020
APR_KEYBASED APR正在使用共享key作为共享内存的key 70021
APR_EINIT   70022
APR_ENOTIMPL 在该平台上,该APR函数尚未实现 70023
APR_EMISMATCH 输入的两个密码不匹配 70024
APR_EABSOLUTE 给定的路径值是绝对路径 70019
APR_ERELATIVE 给定的路径是相对路径 70020
APR_EINCOMPLETE 给定的路径既不是相对路径也不是绝对路径 70021
APR_EABOVEROOT 给定的路径在跟路径上 70022
APR_EBUSY 给定的互斥锁正忙,已经被锁定 70025
APR_EPROC_UNKNOWN 该进程无法被APR所识别 70024

有一点必须明确的是返回码并不一定总是错误码。如果你的函数返回多个值,它们中的每一个都意味着执行成功,但是它们的值却不一样,或者你的函数仅仅返回成功或者失败两种情况,那么APR中通常仍然会返回一个apr_status_t值。
在第一种情况下,即如果执行成功具有多种状态,那么可以为每一个状态定义一个APR返回状态码,典型的例子就是apr_proc_wait函数,它用于等待子进程结束,它会返回三种情况:APR_CHILDDONE表示子进程已经执行完毕;APR_CHILDNOTDONE表示子进程尚未执行完毕;错误码则意味着等待子进程失败。对于前两种返回不能称之为失败,它们都是成功返回,只是返回状态不一样而已,为此APR中定义两个状态码表示返回状态,记住不是错误码。
对于第二种情况,即执行成功后仅有一种状态,那么如果执行成功,APR中通常返回APR_SUCCESS,而不是什么都不返回;如果执行失败,则定义新的APR状态码来描述这种失败。比如apr_compare_users函数,它返回APR_SUCCESS表示失败,同时定义APR_EMISMATCH和其余错误码表示失败。
根据上面的原则,你就会发现APR中的函数很少有返回类型为void或者void*的。更多的都是返回apr_status_t。
APR中所有的错误码的定义在apr_errno.h中都可以找到。当APR函数中发生错误的时候,这些函数必须返回一个错误码。如果这些错误是在系统调用错误,那么APR将使用系统调用返回的错误码errno作为返回码原样返回,比如:
if(open(fname,oflags,0777)<0)
    return errno;
对于系统调用,除了直接返回系统错误码之外,另外一种策略就是使用新的APR错误码替代原始的系统错误码,比如:
if (CreateFile(fname, oflags, sharemod, NULL,
      createflags, attributes, 0) == INVALID_HANDLE_VALUE
return (GetLAstError() + APR_OS_START_SYSERR);
上面的两个例子在不同的平台上实现了相同的功能。显而易见,即使在两个平台上存在的潜在问题都是一样的,那么也会导致返回截然不同的错误码。不过对于这两种情况APR都是支持的。事实上APR的观点是,当一个错误发生的时候,程序通常是记录该错误并且继续往下执行,默认情况下它并不会去尝试解决发生的错误。不过这并不意味着APR根本不捕获错误并且提供解决方案。事实上,在后面的部分我们会看到APR中如何处理错误。
1.5.3.2 返回码信息映射
大部分情况下,状态码主要用于系统内部使用,因此它的含义隐晦,对于用户影响不是特别的大,但是错误码则不一样。用户更多的是希望系统返回足够多的信息以便直到发生错误的原因,从而进行跟踪和调试。因此这种情况下,如果如果仅仅返回一个整数给用户,用户可能会莫名其妙,一头雾水。最好的方法就是能够将该返回码所代表的实际的含义以字符串的形式返回出去。事实上大部分操作系统平台都提供了这种对应函数,比如stderror。APR中使用apr_strerror函数将上述的返回码转换为实际的字符串信息:
APR_DECLARE(char *) apr_strerror(apr_status_t statcode, char *buf,
                                 apr_size_t bufsize)
statcode是需要转换的返回码,转换后的字符串保存在缓冲区buf中,函数所允许的字符串的长度由bufsize控制。
对于不同的返回码,APR采取不同的转换策略:
■ 系统错误码(statcode< APR_OS_START_ERROR)
这类错误码APR中称之为”native error”,即这些错误码并不是APR定义的,而是系统返回的。比如open函数的返回码就应该算”native error”。对于这类错误码,APR的处理就是直接使用stderror_r函数返回系统提示的消息,如果找不到该系统错误码,返回”APR does not understand this error code”信息。之所以不使用stderror是因为stderror不是线程安全的。如果平台能够实现线程安全的stderror函数,比如Solaris, OS/390,那么你也可以使用stderror,否则不要这么做。
这类”native error”通常小于20000,因此它们会使用APR_OS_START_ERROR之前的空隙。
■ APR定义返回码(APR_OS_START_ERROR <= statcode <= APR_OS_START_USERERR)
这类返回码通常是APR定义并使用的,因此APR本身必须能够维持这些返回码和返回信息之间的对应关系。APR中采取的是最原始的”switch/case”方法来对传入的返回码逐一进行判断:
static char *apr_error_string(apr_status_t statcode)
{
    switch (statcode) {
    case APR_ENOPOOL:
        return "A new pool could not be created.";
    case APR_EBADDATE:
        return "An invalid date has been provided";
    case APR_EINVALSOCK:
        return "An invalid socket was returned";
    case APR_ENOPROC:
        return "No process was provided and one was required.";
    ......
}
因此如果需要了解APR_ENOPOOL返回码的确切含义,只需要使用语句printf(“%s”,apr_error_string(APR_ENOPOOL))即可。
■ APR自定义返回码(APR_OS_START_USERERR <= statcode <= APR_OS_START_EAIERR)
■ EAI错误码(APR_OS_START_EAIERR <= statcode <= APR_OS_START_SYSERR)
该范围内的错误码主要对应getaddrinfo函数返回的EAI_*系列错误码。因此在对statcode进行调整后直接调用gai_strerror()返回对应的信息即。如果操作系统不支持gai_strerror函数,那么直接返回"APR does not understand this error code"。
■ 平台相关错误码(APR_OS_START_SYSERR <= statcode)
这类错误码总是与平台相关的,APR返回码与实际的错误码之间保持下面的关系:
APR_Error = APR_OS_START_SYSERR + 系统错误码
由于这类错误码与平台相关,因此处理也是与平台相关的。
1)、对于WINDOW平台而言,所有的Window平台所特有的错误码及实际含义都保存在数组gaErrorList中:
static const struct {
    apr_status_t code;
    const char *msg;
} gaErrorList[] = {
    WSAEINTR,           "Interrupted system call",
    WSAEBADF,           "Bad file number",
    WSAEACCES,          "Permission denied",
    ……
}
因此处理过程就很简单,一旦获取了错误码比如WSAEACCES后通过查找gaErrorList数组就可以获取其实际含义。返回后的字符串使用FormatMessage格式化输出。
2)、对于Unix平台而言主要是由于Linux在调用诸如gethostbyname或者gethostbyaddr等函数时候会返回一些特有的错误码,比如HOST_NOT_FOUND、NO_ADDRESS、NO_DATA、NO_RECOVERY、TRY_AGAIN等。Linux专门提供了hstrerror函数获取这些错误码的实际含义。因此对这类错误码的处理如果系统实现了hstrerror,则调用hstrerror处理,否则使用最原始的switch/case进行处理:
switch(err) {
    case HOST_NOT_FOUND:
        msg = "Unknown host";
        break;
    case NO_ADDRESS:
        msg = "No address for host";
        break;
    case NO_DATA:
        msg = "No address for host";
        break;
    default:
        msg = "Unrecognized resolver error";
    }
3)、另一种需要考虑的就是OS/2。由于OS/2不是我们的重点,此处不再描述。需要了解的可以参考源代码。
1.5.3.3 错误捕捉策略
对于APR自定义错误码,APR可以直接通过返回值获取,而对于系统调用错误码,Apache则必须使用系统函数来获取。对于所有的操作系统平台,Apache提供了四个函数捕获系统相关错误码:
apr_get_os_error()
apr_set_os_error(e)
apr_get_netos_error()
apr_set_netos_error(e)
前两个分别用于或者和设置系统错误码,而后者则主要用于获取和设置网络错误码,之所以进行这种区分,是因为大部分操作系统平台上获取系统错误码和获取网络错误码的方法不同。在Window平台上获取错误码主要是调用函数GetLastError和SetLastError,对于网络错误的获取则是WSAGetLastError和WSASetLastError。Unix下则与之不同。下表列出了各个平台对应的处理:
  apr_get_os_error apr_set_os_error(e) apr_get_netos_error apr_set_netos_error(e)
Window GetLastError SetLastError(e) WSAGetLastError WSASetLastError
Unix (errno) (errno = (e)) (errno) (errno = (e))
Netware (errno) (errno = (e)) WSAGetLastError WSASetLastError

Apache中大部分的操作都是将返回码与指定的返回码进行比较,然后根据比较结果做出进一步的操作。不过Apache中对于错误码的比较并不是使用最简单的格式,比如“if(s==APR_EBUSY)”,取而代之的是,Apache中使用一些简单的宏来执行比较任务,通常格式为:
APR_STATUS_IS_XXX
XXX通常是错误码APR_XXX的XXX部分,比如APR_STATUS_IS_EOF、APR_STATUS_IS_BADCH等等。而这些宏的实现非常简单,一看就明白:
#define APR_STATUS_IS_BADCH(s)          ((s) == APR_BADCH)
APR_errno.h中的大部分内容就是这种宏定义,每一个错误码都会对应这样一个宏定义。
另外系统中还定义了两个宏分别用于实现APR返回码与操作系统本地码的转换:
#define APR_FROM_OS_ERROR(e) (e == 0 ? APR_SUCCESS : e + APR_OS_START_SYSERR)
#define APR_TO_OS_ERROR(e) (e == 0 ? APR_SUCCESS : e - APR_OS_START_SYSERR)
前者用于将操作系统平台相关的返回码转换为APR定义的返回码,而后者则相反,用于将APR定义的返回码转换为操作系统平台相关码。
由于APR是可移植的,这样就可能遇到这样一个问题:不同平台错误码的不一致。如何处理呢?APR给我们提供了2种策略:
a)对于所有的操作系统平台都返回相同的错误码
这种策略的缺点是从平台相关错误码转换为通用错误码比较耗费时间,而且大多数情况下,开发人员需要的仅仅是输出一个错误字符串而已。如果我们将所有的错误码转换为一个通用的公共的错误码子集,那么为了输出一个错误字符串信息,我们必须完成四个步骤:
    make syscall that fails
        convert to common error code                 step 1
        return common error code
            check for success
            call error output function               step 2
                convert back to system error         step 3
                output error string                  step 4
相比而言,这是一个比较耗时的步骤。通过使用使用平台相关错误码的话,那么整个步骤可以压缩为只有两步:
    make syscall that fails
        return error code
            check for success
            call error output function               step 1
                output error string                  step 2
这种策略的第二个可能造成的问题就是错误码的损耗,这种问题源自各个平台错误码数目的不均衡性。比如Windows和OS/2操作系统定义了成百上千错误码,而POSIX才定义了50错误码,如果都转换为规范统一的错误码,势必会有Window平台错误码含义的丢失(错误码多的比如Window平台),或者可能得不到拥有真正含义的错误码(错误码少的,比如POSIX)。
b) 返回平台相关错误码,如果需要将它转换为APR通用错误码
第二种策略中,程序的执行路线往往要根据函数返回错误码来定,这么做的缺点就是把这些工作推给了程序员。执行流程如:
make syscall that fails
        convert to common error code
        return common error code
            decide execution based on common error code
如果考虑到将平台相关的错误码转换为通用的错误码,那么上面的代码段可以修改为如下:
make syscall that fails
        return error code
            convert to common error code (using ap_canonical_error)
            decide execution based on common error code
1.5.4宏处理
Apache目前能够支持五个大种类的运行平台,包括Window、OS/2、BeOS、Unix、NetWare,而Window又可以细分为Window98、Window2000等等。Unix则又可以进一步细分,包括Linux,ScoUNIX,DARWIN等等。为了能够让Apache运行在如此之多的操作系统平台上,Apache在源代码中增加了许多的编译开关。
举个例子,比如utime.h头文件的包含问题。因为文件在Linux(gcc)下面和Windows(cl)下所处的C Library目录不同。包含的处理办法就不一样。可能需要这样写才能完全正确的包含。
#if HAVE_UTIME_H                   //---- 如果有utime.h 文件
#    ifdef WIN32                   //-----如果是win32环境   
#        include <sys/utime.h>    //-----包含sys/utime.h
#    endif
#    ifdef LINUX                   //---- 如果是Linux环境
#        include <utime.h>        //---- 包含utime.h   
#    endif
#else                              //--- 如果没有utime.h定义出需要的结构   
struct utimbuf
{
long actime;
long modtime;
};
#endif
Apache处理与之类似。根据编译环境的不同来编译不同的代码。 这样的#define的区隔,主要就是为了区隔不同平台的不同细微区别。有的区别也许是某些常量没有定义,有些区别是某些函数不存在。
Apache中使用的很多的编译开关是各个操作系统或者各个编译器已经确定的,通过这些预定义就可以很容易的区分使用的操作系统平台,比如__osf__和__alpha是DEC的OSF/1 1.3操作系统中的定义,因此如果某个函数只能运行于OSF/1 1.3中,则可以使用下面的编译处理代码:
#ifdefine __osf__||__aplpa
        //调用函数
#endif
下面的表格中给出了目前大部分的操作系统以及编译器的编译开关:


机器硬件     生产商            操作系统                     编译器         能够识别的编译其开关变量
AMIGA        Commodore          AMIGA-OS (AMIGADOS)           GNU           amiga or AMIGA, __GNUC__, maybe MC68000 or AMIGA3000
any          any                UNIX                          GNU           unix, __GNUC__, ...
any          any                UNIX                          CC            unix, ...
Amiga 3000   Commodore          Amiga UNIX 2.1 SVR4.0         GNU           unix,__unix__,AMIX,__AMIX__,__svr4__,m68k, __m68k__, __motorola__, __GNUC__
SUN-3        Sun                SUN-OS3 (UNIX BSD 4.2)        GNU           sun, unix, mc68020, __GNUC__
SUN-3        Sun                SUN-OS4 (UNIX SUNOS 4.1)      GNU           sun, unix, mc68020, __GNUC__
SUN-386      Sun                SUN-OS4 (UNIX SUNOS 4.0)      GNU           sun, unix, sun386, i386, __GNUC__
SUN-386      Sun                SUN-OS4 (UNIX SUNOS 4.0)      CC            sun, unix, sun386, i386
SUN-4        Sun                SUN-OS4 (UNIX SUNOS 4.1)      GNU           sun, unix, sparc, __GNUC__
SUN-4        Sun                SUN-OS4 (UNIX SUNOS 4.1)      CC            sun, unix, sparc
SUN-4        Sun                SUN-OS5 (UNIX Solaris)        GCC           sun, unix, sparc, __GNUC__
UltraSparc   Sun                Solaris 7 (UNIX SUNOS 5.7)    CC            sun, unix, __sparc, __sparcv9
UltraSparc   Sun                Solaris 7 (UNIX SUNOS 5.7)    GCC           sun, unix, __sparc, __arch64__, __GNUC__
IBM-PC/386   any                SUN-OS5 (UNIX Solaris)        GCC           sun, unix, __svr4__, i386, __GNUC__
HP9000-300   Hewlett-Packard    NetBSD 0.9 (UNIX BSD 4.3)     GNU           unix, __NetBSD__, mc68000, __GNUC__
HP9000-300   Hewlett-Packard    HP-UX 8.0 (UNIX SYS V)        GNU           [__]hpux, [__]unix, [__]hp9000s300, mc68000, __GNUC__
HP9000-800   Hewlett-Packard    HP-UX 8.0 (UNIX SYS V)        GNU           [__]hpux, [__]unix, [__]hp9000s800
IRIS         Silicon Graphics   IRIX (UNIX SYS V 3.2)         GNU           unix, SVR3, mips, sgi, __GNUC__
IRIS         Silicon Graphics   IRIX (UNIX SYS V)             cc -ansi      [__]unix, [__]SVR3, [__]mips, [__]sgi
IRIS         Silicon Graphics   IRIX 5 (UNIX SYS V 4)         GNU           [__]unix, [__]SYSTYPE_SVR4, [__]mips, [__]host_mips, [__]MIPSEB, [__]sgi, _
DSO__, [__]_MODERN_C, __GNUC__
DECstation 5000                 RISC/OS (Ultrix V4.2A)        GNU           unix, [__]mips, [__]ultrix
DG-UX 88k    Data General       DG/UX                         GNU           unix, m88000, DGUX
DEC Alpha    DEC                OSF/1 1.3                     cc            [unix,] __unix__, __osf__, __alpha
DEC Alpha    DEC                OSF/1 1.3                     GNU           unix, __unix__, __osf__, __alpha, __alpha__, _LONGLONG
Apple MacII  Apple              A/UX (UNIX SYS V 2)           GNU           [__]unix, [__]AUX, [__]macII, [__]m68k, mc68020, mc68881, __GNUC__
NeXT         NeXT               NeXTstep 3.1 (UNIX)           cc            NeXT, m68k; NEXTAPP for NeXTstep Application
PowerPC      Apple              Mach 3.0 + MkLinux            GNU           unix, __powerpc__, __PPC__, _ARCH_PPC, _CALL_SYSV, __ELF__, __linux__
PowerPC      Apple              Mach + Rhapsody               cc            __MACH__, __APPLE__, __ppc[__], __GNUC__, __APPLE_CC__
PowerPC      Apple              Mach + MacOS X                cc            __MACH__, __APPLE__, __ppc__, __GNUC__, __APPLE_CC__
Sequent      Sequent            PTX 3.2.0 V2.1.0 i386 (SYS V) GNU           unix, i386, _SEQUENT_, __GNUC__
Sequent      Sequent            PTX V4.1.3                    GNU           unix, i386, _SEQUENT_, __svr4__, __GNUC__
Convex C2    Convex             ConvexOS 10.1                 GNU           __convex__, __GNUC__
IBM RS/6000  IBM                AIX 3.2                       GNU           _AIX, _AIX32, _IBMR2, __CHAR_UNSIGNED__, __GNUC__
IBM-PC/386   any                LINUX (free UNIX)             GNU           unix, linux, i386, __GNUC__
IBM-PC/386   any                LINUX (free UNIX)             Intel 5.0     __unix__, __linux__, __INTEL_COMPILER, __ICC, __USLC__
IBM-PC/386   any                386BSD 0.1 (UNIX BSD 4.2)     GNU           unix, __386BSD__, i386, __GNUC__
IBM-PC/386   any                NetBSD 0.9 (UNIX BSD 4.3)     GNU           unix, __NetBSD__, i386, __GNUC__
IBM-PC/386   any                FreeBSD 4.0 (UNIX BSD 4.4)    GNU           unix, __FreeBSD__, i386, __GNUC__
IBM-PC/386   any                EMX 0.9c (UNIXlike on OS/2)   GNU           [unix,] i386, __GNUC__, __EMX__
IBM-PC/386   any                Cygwin32 on WinNT/Win95       GNU           _WIN32, __WINNT__, __CYGWIN32__, __POSIX__, _X86_, i386, __GNUC__
IBM-PC/386   any                Mingw32 on WinNT/Win95        GNU           _WIN32, __WINNT__, __MINGW32__, _X86_, i386, __GNUC__
IBM-PC/386   any                WinNT/Win95                   MSVC4.0,5.0   _WIN32, _M_IX86, _MSC_VER
IBM-PC/386   any                WinNT/Win95                   Borland 5.0   __WIN32__, _M_IX86, __TURBOC__, __BORLANDC__
IBM-PC/386   any                WinNT/Win95 and Cygwin32      GNU           _WIN32, __WINNT__, __CYGWIN32__, __POSIX__, __i386__, _X86_, __GNUC__
IBM-PC/586   any                BeOS 5                        GNU           __BEOS__, __INTEL__, __i386__, _X86_, __GNUC__
IBM-PC/586   any                HP NUE/ski, Linux             GNU           unix, linux, __ia64[__], __GNUC__, __LP64__
RM400        Siemens-Nixdorf    SINIX-N 5.42                  c89           unix, mips, MIPSEB, host_mips, sinix, SNI, _XPG_IV
Acorn        Risc PC            RISC OS 3.x                   GNU           [__]arm, [__]riscos, __GNUC__
Acorn        Risc PC            RISC OS 3.x                   Norcroft      [__]arm, [__]riscos
APPLE IIGS   Apple              ??                            ??







当然,列出上面的大部分的编译开关,并不是说Apache都支持它们,事实上Apache仅仅支持一部分,如果仅仅对某个操作系统或者某个机器有兴趣,则可以挑选对应的宏定义中的代码。
上述的编译器开发都是某个平台相关的,事实上只要运行于该平台,该开关自然就成立,不需要APR本身重新定义。另外有一些特性开关则是必须由APR自行定义。这些特性通常是操作系统之间很小的区别,比如同样是Unix系统,可能有的支持共享内存,有的不支持,为此在使用共享内存之前必须能够判断当前的平台是否支持。这些特性宏的定义可以从apr.h.in模板中定义:
#define APR_HAVE_ARPA_INET_H     @arpa_ineth@
#define APR_HAVE_CONIO_H         @conioh@
#define APR_HAVE_CRYPT_H         @crypth@
#define APR_HAVE_CTYPE_H         @ctypeh@
#define APR_HAVE_DIRENT_H        @direnth@
#define APR_HAVE_ERRNO_H         @errnoh@
#define APR_HAVE_FCNTL_H         @fcntlh@
#define APR_HAVE_IO_H            @ioh@
#define APR_HAVE_LIMITS_H        @limitsh@
#define APR_HAVE_NETDB_H         @netdbh@
#define APR_HAVE_NETINET_IN_H    @netinet_inh@
#define APR_HAVE_NETINET_SCTP_H @netinet_sctph@
#define APR_HAVE_NETINET_SCTP_UIO_H @netinet_sctp_uioh@
#define APR_HAVE_NETINET_TCP_H   @netinet_tcph@
#define APR_HAVE_PTHREAD_H       @pthreadh@
#define APR_HAVE_SEMAPHORE_H     @semaphoreh@
#define APR_HAVE_SIGNAL_H        @signalh@
#define APR_HAVE_STDARG_H        @stdargh@
#define APR_HAVE_STDINT_H        @stdint@
#define APR_HAVE_STDIO_H         @stdioh@
#define APR_HAVE_STDLIB_H        @stdlibh@
#define APR_HAVE_STRING_H        @stringh@
#define APR_HAVE_STRINGS_H       @stringsh@
#define APR_HAVE_SYS_IOCTL_H     @sys_ioctlh@
#define APR_HAVE_SYS_SENDFILE_H @sys_sendfileh@
#define APR_HAVE_SYS_SIGNAL_H    @sys_signalh@
#define APR_HAVE_SYS_SOCKET_H    @sys_socketh@
#define APR_HAVE_SYS_SOCKIO_H    @sys_sockioh@
#define APR_HAVE_SYS_SYSLIMITS_H @sys_syslimitsh@
#define APR_HAVE_SYS_TIME_H      @sys_timeh@
#define APR_HAVE_SYS_TYPES_H     @sys_typesh@
#define APR_HAVE_SYS_UIO_H       @sys_uioh@
#define APR_HAVE_SYS_UN_H        @sys_unh@
#define APR_HAVE_SYS_WAIT_H      @sys_waith@
#define APR_HAVE_TIME_H          @timeh@
#define APR_HAVE_UNISTD_H        @unistdh@
#define APR_HAVE_SHMEM_MMAP_TMP     @havemmaptmp@
#define APR_HAVE_SHMEM_MMAP_SHM     @havemmapshm@
#define APR_HAVE_SHMEM_MMAP_ZERO    @havemmapzero@
#define APR_HAVE_SHMEM_SHMGET_ANON @haveshmgetanon@
#define APR_HAVE_SHMEM_SHMGET       @haveshmget@
#define APR_HAVE_SHMEM_MMAP_ANON    @havemmapanon@
#define APR_HAVE_SHMEM_BEOS         @havebeosarea@

#define APR_USE_SHMEM_MMAP_TMP     @usemmaptmp@
#define APR_USE_SHMEM_MMAP_SHM     @usemmapshm@
#define APR_USE_SHMEM_MMAP_ZERO    @usemmapzero@
#define APR_USE_SHMEM_SHMGET_ANON @useshmgetanon@
#define APR_USE_SHMEM_SHMGET       @useshmget@
#define APR_USE_SHMEM_MMAP_ANON    @usemmapanon@
#define APR_USE_SHMEM_BEOS         @usebeosarea@ #define APR_USE_FLOCK_SERIALIZE           @flockser@
#define APR_USE_SYSVSEM_SERIALIZE         @sysvser@
#define APR_USE_POSIXSEM_SERIALIZE        @posixser@
#define APR_USE_FCNTL_SERIALIZE           @fcntlser@
#defineAPR_USE_PROC_PTHREAD_SERIALIZE @procpthreadser@
#define APR_USE_PTHREAD_SERIALIZE    @pthreadser@

#define APR_HAS_FLOCK_SERIALIZE           @hasflockser@
#define APR_HAS_SYSVSEM_SERIALIZE         @hassysvser@
#define APR_HAS_POSIXSEM_SERIALIZE        @hasposixser@
#define APR_HAS_FCNTL_SERIALIZE           @hasfcntlser@
#define APR_HAS_PROC_PTHREAD_SERIALIZE @hasprocpthreadser@
#define APR_HAS_RWLOCK_SERIALIZE      @hasrwlockser@
#define APR_PROCESS_LOCK_IS_GLOBAL @proclockglobal@
#define APR_HAVE_CORKABLE_TCP   @have_corkable_tcp@
#define APR_HAVE_GETRLIMIT      @have_getrlimit@
#define APR_HAVE_IN_ADDR        @have_in_addr@
#define APR_HAVE_INET_ADDR      @have_inet_addr@
#define APR_HAVE_INET_NETWORK   @have_inet_network@
#define APR_HAVE_IPV6           @have_ipv6@
#define APR_HAVE_MEMMOVE        @have_memmove@
#define APR_HAVE_SETRLIMIT      @have_setrlimit@
#define APR_HAVE_SIGACTION      @have_sigaction@
#define APR_HAVE_SIGSUSPEND     @have_sigsuspend@
#define APR_HAVE_SIGWAIT        @have_sigwait@
#define APR_HAVE_STRCASECMP     @have_strcasecmp@
#define APR_HAVE_STRDUP         @have_strdup@
#define APR_HAVE_STRICMP        @have_stricmp@
#define APR_HAVE_STRNCASECMP    @have_strncasecmp@
#define APR_HAVE_STRNICMP       @have_strnicmp@
#define APR_HAVE_STRSTR         @have_strstr@
#define APR_HAVE_MEMCHR         @have_memchr@
#define APR_HAVE_STRUCT_RLIMIT @struct_rlimit@
#define APR_HAVE_UNION_SEMUN    @have_union_semun@
#define APR_HAVE_SCTP           @have_sctp@

/* APR Feature Macros */
#define APR_HAS_SHARED_MEMORY     @sharedmem@
#define APR_HAS_THREADS           @threads@
#define APR_HAS_SENDFILE          @sendfile@
#define APR_HAS_MMAP              @mmap@
#define APR_HAS_FORK              @fork@
#define APR_HAS_RANDOM            @rand@
#define APR_HAS_OTHER_CHILD       @oc@
#define APR_HAS_DSO               @aprdso@
#define APR_HAS_SO_ACCEPTFILTER   @acceptfilter@
#define APR_HAS_UNICODE_FS        0
#define APR_HAS_PROC_INVOKED      0
#define APR_HAS_USER              1
#define APR_HAS_LARGE_FILES       0
#define APR_HAS_XTHREAD_FILES     0
#define APR_HAS_OS_UUID           0

在使用configure进行配置的时候,apr.h.in模板作为输入最终生成apr.h文件。不过apr.h.in中的@xx@将被0或者1所取代:如果该平台支持某个特定,相应的宏将定义为1,否则定义为0。

作者: 蟋蟀   发布时间: 2007-08-01

Apache 中使用的 APR Memory Pool 分析

简介
APR 中的Memory Pool是内存管理基础模块,所有其他的模块,只要用到的内存分配,都要用到这个模块,相关的结构中都有apr_pool_t参数
pool本身并不直接从物理内存中分配或释放,而是通过allocator(内存分配器)来统一管理,可以为新池创建新的allocator(内存分配器),但通常使用默认的全局allocator(内存分配器),这样更有助于统一的内存管理
pool采用的是树形的结构,在初始化内存池(apr_pool_initialize)时,建立根池,和全局allocator(内存分配器),以后建立的都是根结点的子孙结点
可以从pool中分配任何大小的内存块,但释放的单位为pool,就是说pool释放之前,从pool分配出的内存不能单独释放,看起来好像有点浪费.
这里要注意的是,有些分配的内存块,清除时有特别操作,这样就需要要带清除函数,在分配之后用apr_pool_cleanup_register注册清除时用的函数。
特殊的,如果内存块里是线程对象,也不能用一般的清除函数,应该用apr_pool_note_subprocess注册清除操作

[结构体]
//池的结构
struct apr_pool_t {
    apr_pool_t           *parent;    //父池结点
    apr_pool_t           *child;    //子池结点
    apr_pool_t           *sibling;    //兄弟池结点
    apr_pool_t          **ref;        //指向-指向自已的指针,一般是前继结点的next指针
    cleanup_t            *cleanups;    //需要清除函数来销毁的内存块指针列表,通过注册来加入的
    cleanup_t            *free_cleanups;//当Kill cleanups中的一个项时,放到这里,注册一个新的项时,先从这里利用
    apr_allocator_t      *allocator;    //内存分配器,一般就用默认的,所有池共享一个内存分配器
    struct process_chain *subprocesses;    //子线程
    apr_abortfunc_t       abort_fn;        
    apr_hash_t           *user_data;
    const char           *tag;

#if !APR_POOL_DEBUG
    apr_memnode_t        *active;    //拥有的内存结点
    apr_memnode_t        *self; /* The node containing the pool itself */
    char                 *self_first_avail;

#else /* APR_POOL_DEBUG */
    apr_pool_t           *joined; /* the caller has guaranteed that this pool
                                   * will survive as long as ->joined */
    debug_node_t         *nodes;
    const char           *file_line;
    apr_uint32_t          creation_flags;
    unsigned int          stat_alloc;
    unsigned int          stat_total_alloc;
    unsigned int          stat_clear;
#if APR_HAS_THREADS
    apr_os_thread_t       owner;
    apr_thread_mutex_t   *mutex;
#endif /* APR_HAS_THREADS */
#endif /* APR_POOL_DEBUG */
#ifdef NETWARE
    apr_os_proc_t         owner_proc;
#endif /* defined(NETWARE) */
};

[函数]
对系统内存池初始化,全局的,一个进程只要初始化一次
apr_status_t     apr_pool_initialize (void)
销毁内存池对象,及内部的结构和子内存池
void         apr_pool_terminate (void)

创建一个新的内存池
apr_status_t     apr_pool_create_ex (apr_pool_t **newpool, apr_pool_t *parent, apr_abortfunc_t abort_fn, apr_allocator_t *allocator)
创建一个新的内存池,apr_pool_create_ex的使用默认参数简化版
apr_status_t     apr_pool_create (apr_pool_t **newpool, apr_pool_t *parent)
获取内存池使用的内存分配器
apr_allocator_t *     apr_pool_allocator_get (apr_pool_t *pool)
清除一个内存池的内容,清除后内容为空,但可以再使用
void             apr_pool_clear (apr_pool_t *p)
释构一个内存池
void             apr_pool_destroy (apr_pool_t *p)

从池中分配内存
void *             apr_palloc (apr_pool_t *p, apr_size_t size)
从池中分配内存,并将分配出来的内存置0
void *             apr_pcalloc (apr_pool_t *p, apr_size_t size)

设置内存分配出错时的调用函数
void             apr_pool_abort_set (apr_abortfunc_t abortfunc, apr_pool_t *pool)
获取内存分配出错时的调用函数
apr_abortfunc_t     apr_pool_abort_get (apr_pool_t *pool)

获取池的父池
apr_pool_t *     apr_pool_parent_get (apr_pool_t *pool)

判断a是否是b的祖先
int             apr_pool_is_ancestor (apr_pool_t *a, apr_pool_t *b)

为内存池做标签
void             apr_pool_tag (apr_pool_t *pool, const char *tag)

设置与当前池关联的数据
apr_status_t     apr_pool_userdata_set (const void *data, const char *key, apr_status_t(*cleanup)(void *), apr_pool_t *pool)
设置与当前池关联的数据,与apr_pool_userdata_set类似,但内部不拷贝数据的备份,如常量字符串时就有用
apr_status_t     apr_pool_userdata_setn (const void *data, const char *key, apr_status_t(*cleanup)(void *), apr_pool_t *pool)
获取与当前池关联的数据
apr_status_t     apr_pool_userdata_get (void **data, const char *key, apr_pool_t *pool)
注册内存块的清除函数,每块销毁时要特别处理的都要注册下,在cleanups里加入一个项
void             apr_pool_cleanup_register (apr_pool_t *p, const void *data, apr_status_t(*plain_cleanup)(void *), apr_status_t(*child_cleanup)(void *))
删除内存块的清除函数,从cleanups里移除一个项,放入free_cleanups中
void             apr_pool_cleanup_kill (apr_pool_t *p, const void *data, apr_status_t(*cleanup)(void *))
用新的child_cleanup,替换原来老的child_cleanup
void             apr_pool_child_cleanup_set (apr_pool_t *p, const void *data, apr_status_t(*plain_cleanup)(void *), apr_status_t(*child_cleanup)(void *))
执行内存块的清除函数,进从清除函数的队列cleanups中删除
apr_status_t     apr_pool_cleanup_run (apr_pool_t *p, void *data, apr_status_t(*cleanup)(void *))
一个空的内存块清除函数
apr_status_t     apr_pool_cleanup_null (void *data)
执行所有的子清除函数child_cleanup
void             apr_pool_cleanup_for_exec (void)

带调试信息内存池函数,功能跟上面的一样,只是多了调试信息
apr_status_t     apr_pool_create_ex_debug (apr_pool_t **newpool, apr_pool_t *parent, apr_abortfunc_t abort_fn, apr_allocator_t *allocator, const char *file_line)
void             apr_pool_clear_debug (apr_pool_t *p, const char *file_line)
void             apr_pool_destroy_debug (apr_pool_t *p, const char *file_line)
void *             apr_palloc_debug (apr_pool_t *p, apr_size_t size, const char *file_line)
void *             apr_pcalloc_debug (apr_pool_t *p, apr_size_t size, const char *file_line)

<一>内存池初始化 apr_pool_initialize
1.一个进程只在第一次调用时初始化,后面只记录调用的次数
    if (apr_pools_initialized++)
        return APR_SUCCESS;
2.创建全局内存分配器(global_allocator)
      if ((rv = apr_allocator_create(&global_allocator)) != APR_SUCCESS) {
        apr_pools_initialized = 0;
        return rv;
    }
3.创建全局内存池,根内存池
      if ((rv = apr_pool_create_ex(&global_pool, NULL, NULL,
                                 global_allocator)) != APR_SUCCESS) {
        apr_allocator_destroy(global_allocator);
        global_allocator = NULL;
        apr_pools_initialized = 0;
        return rv;
    }
4.为内存池做标记
   apr_pool_tag(global_pool, "apr_global_pool");
5.初始化原子操作
有什么想法可以发邮件 fastxyf at hotmail.com,一起交流
6.对于支持多线程的系统,还创建互拆体,并设置到内存分配器
#if APR_HAS_THREADS
    {
        apr_thread_mutex_t *mutex;

        if ((rv = apr_thread_mutex_create(&mutex,
                                          APR_THREAD_MUTEX_DEFAULT,
                                          global_pool)) != APR_SUCCESS) {
            return rv;
        }

        apr_allocator_mutex_set(global_allocator, mutex);
    }
#endif /* APR_HAS_THREADS */

7.设置内存分配器的池所有者
    apr_allocator_owner_set(global_allocator, global_pool);

<二>销毁内存池对象 apr_pool_terminate
1.如果调用了多次内存池初始化,则只有在调用了相应次数的销毁时,最后一次才进行操作
    if (!apr_pools_initialized)
        return;
    if (--apr_pools_initialized)
        return;
2.释放全局内存池,同时会释放想应的互拆体,如果内存分配器的所有者是本内存池,则一同释放内存分配器
    apr_pool_destroy(global_pool);

<三>创建一个新的内存池 apr_pool_create_ex
参数说明:
    newpool     新创建的内存池指针,用于返回的.
    parent         父内存池. 如果为空,则父内存池为根内存池,非空时,新的内存池继承父内存池的属性.
    abort_fn     分配内存失败时的回调函数,中断函数
    allocator     新内存池所用的内存分配器. NULL则使用父内存池的内存分配器
1.先确定内存池的参数,如父结点,出错处理函数,所用的内存分配器
    if (!parent)
        parent = global_pool;
    if (!abort_fn && parent)
        abort_fn = parent->abort_fn;
    if (allocator == NULL)
        allocator = parent->allocator;
2.从内存分配器一块内存块,用于存贮内存池的信息,如果内存分配失败,则调用中断函数
    if ((node = allocator_alloc(allocator,
                                MIN_ALLOC - APR_MEMNODE_T_SIZE)) == NULL) {
        if (abort_fn)
            abort_fn(APR_ENOMEM);

        return APR_ENOMEM;
    }
3.用新分配内存块,存贮内存池的结构apr_pool_t
    node->next = node;
    node->ref = &node->next;

    pool = (apr_pool_t *)node->first_avail;
    node->first_avail = pool->self_first_avail = (char *)pool + SIZEOF_POOL_T;

    pool->allocator = allocator;
    pool->active = pool->self = node;
    pool->abort_fn = abort_fn;
    pool->child = NULL;
    pool->cleanups = NULL;
    pool->free_cleanups = NULL;
    pool->subprocesses = NULL;
    pool->user_data = NULL;
    pool->tag = NULL;
  物理内存块的内容如下
           |----------------|
           | apr_memnode_t  | 占用大小 APR_MEMNODE_T_SIZE
           | ---------------|
           | apr_pool_t     | 占用大小 SIZEOF_POOL_T
           | ---------------|
           |   可用空间     | 占用大小 8K - APR_MEMNODE_T_SIZE - SIZEOF_POOL_T
           | ---------------|
4.系统支持多线程,则创建互拆体.
5.设置兄弟结点,及父结点
        if ((pool->sibling = parent->child) != NULL)
            pool->sibling->ref = &pool->sibling;

        parent->child = pool;
        pool->ref = &parent->child;

apr_pool_create 是使用了默认的内存分配器,和中断函数的apr_pool_create_ex简化版
#define apr_pool_create(newpool, parent) \
    apr_pool_create_ex(newpool, parent, NULL, NULL)

<四> 从池中分配一块内存 apr_palloc
参数说明:
    pool        要分配内存的池
    size        要分配的内存大小
1.先按8字节对齐size
    size = APR_ALIGN_DEFAULT(size);
2. 如果首结点有足够的空闲空间,则从这里分配
    if (size < (apr_size_t)(active->endp - active->first_avail)) {
        mem = active->first_avail;
        active->first_avail += size;

        return mem;
    }
3.跟链表首结点比较(因为链表结点是按空闲空间的大小,由多至少排序的,所以首结点不够,后面的也不用比了),空间够就从链表头结点分配,因为分配后链表头结点空闲空间不一定还最多了,所以要取出,后面重新插入
  如果链表首结点空间不够,则调用内存分配器,新分配一块内存
  
    if (size < (apr_size_t)(node->endp - node->first_avail)) {
        list_remove(node);
    }
    else {
        if ((node = allocator_alloc(pool->allocator, size)) == NULL) {
            if (pool->abort_fn)
                pool->abort_fn(APR_ENOMEM);

            return NULL;
        }
    }
4.将首结点或新分配的结点node,插到队列首部,再根据node里的空间大小,选择插到的位置
     这里没看懂,为什么要插入?到后面找又先移出,再插到正确位置.不能改成直接在后面一次插入?
    list_insert(node, active);

    pool->active = node;

    free_index = (APR_ALIGN(active->endp - active->first_avail + 1,
                            BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;

    active->free_index = (APR_UINT32_TRUNC_CAST)free_index;
    node = active->next;
    if (free_index >= node->free_index)
        return mem;

    do {
        node = node->next;
    }
    while (free_index < node->free_index);

    list_remove(active);
    list_insert(active, node);

apr_pcalloc 是在apr_palloc分配的内存上,增加了对分配的内存块置0
    size = APR_ALIGN_DEFAULT(size);
    if ((mem = apr_palloc(pool, size)) != NULL) {
        memset(mem, 0, size);
    }

<五>清空池内容,释放占用的内存,但保留池对象 apr_pool_clear
1.如果存在子池,则释放所有子池
    while (pool->child)
        apr_pool_destroy(pool->child);
2.调用清除函数,清除注册了销毁函数的内存块,每块需要销毁函数的内存块都要单独注册
    run_cleanups(&pool->cleanups);
3.清除包括子线程的内存块
    free_proc_chain(pool->subprocesses);
4.清除附在本内存池上的内存块,回到刚分配内存池的状态
    *active->ref = NULL;
    allocator_free(pool->allocator, active->next);
    active->next = active;
    active->ref = &active->next;

<六>释构一个内存池 apr_pool_destroy
  执行的操作和apr_pool_clear差不多,增加了删除互拆体,和释放内存池结构占用的块
  如果所用的内存分配器的所有者,是本内存池,也一并析构

作者: 蟋蟀   发布时间: 2007-08-01

| 第2章 apache平台和结构《The Apache Modules Book Application Development with Apache》 >>


第3章 Apache可移植运行期库(前四节)《The Apache Modules Book Application Development with Apache》

转载注明出处!
《The Apache Modules Book:Application Development with Apache》Nick Kew
第一版 2007-05-19
翻译难免有错误,因此把英文原文贴上.有翻译错误请与我联系,非常谢谢!
版权所有,yacsha([email protected])
http://blog.csdn.net/yacsha/

第3章 Apache可移植运行期库(翻译)
The Apache Portable Runtime (APR) and Utilities (APR-UTILS or APU) are a pair
of libraries used by the Apache httpd, but autonomously developed and maintained
within the ASF. Although many core developers are involved in both httpd
(the webserver) and APR, the projects are separate. These libraries provide core
functions that are not specific to webserving but are also useful in more general
applications.
Apache可移植运行期库(APR)和实用库(APR-UTILS或者APU)是被Apache httpd使用的一对库文件,但是由ASF(Apache Software Foundation)开发和维护.尽管很多核心开发人员都在httpd(web服务器)和APR项目中,但是这两个项目还是独立的.这些库文件提供的核心功能不仅仅为web服务器也为更多普通的应用程序.

Apart from the webserver, the best-known APR application is Subversion, a revision
and change control management system. Another is Site Valet, a suite of software
for QA and accessibility audit on the Web; Site Valet was developed by this book’s
author.
除web服务器之外,大家都知道的APR程序采用的是Subversion版本控制系统.另一个是Site Valet软件,用来在web上进行质量和访问控制.Site Valet由本书的作者开发.

This chapter discusses the APR as it applies to Apache modules. It does not go into
subjects such as application initialization, which are necessary but are handled
internally by the Apache core code. For developers working outside the webserver
context, this usage is documented clearly within APR itself, and it is covered in
the tutorial at http://dev.ariel-networks.com/apr/apr-tutorial/html/
apr-tutorial.html.
本章讨论APR对Apache模块的应用.不会探究诸如程序初始化之类的话题, 程序初始化是必须的,但是已经被Apache内部核心代码处理了.在web服务器之外进行开发,这个库的使用在APR自身的文档有清晰的说明.在http://dev.ariel-networks.com/apr/apr-tutorial/html/
apr-tutorial.html上也有谈到.

3.1 APR
The main purpose of APR is to provide a portable, platform-independent layer for
applications. Functions such as filesystem access, network programming, process
and thread management, and shared memory are supported in a low-level, crossplatform
library. Apache modules that use exclusively APR instead of native system
functions are portable across platforms and can expect to compile cleanly―or at
worst with a trivial amount of tidying up―on all platforms supported by Apache.
APR的主要目的是为应用程序提供可移植的,平台独立的层.像诸如文件系统的访问,网络编程,进程线程管理和共享内存的功能由底层的,跨平台的库支持. 使用APR库函数代替本地系统函数的Apache模块能在apache支持的平台间移植,也能按期望顺利编译一一或者最坏也只需要一点点的处理.

Each APR module comprises an application programming interface (API) shared
between all platforms, together with implementations of the functions defined in
the API. The implementations are often wholly or partly platform-specific,
although this issue is of no concern to applications.
每一个APR模块由应用编程接口(API)加上这些API的实现组成,这些API在所有平台共享.这些API实现经常是全部的或者部分和平台相关,尽管这个问题对应用程序无关紧要.

At the core of APR is Apache’s resource management (pools), which are discussed in
more detail later in this chapter. Table 3-1 provides a full list of the APR modules.
APR的核心是Apache的资源管理(pools),在这个章节的稍后详细讨论.表3-1提供了一个完整的APR模块列表.

TABLE 3-1
APR Modules
Name                           Purpose
apr_allocator              Used internally for memory allocation
apr_atomic               Atomic operations
apr_dso                        Dynamic loading of code (.so/.dll)
apr_env                       Reading/setting environment variables
apr_errno                     Defines error conditions and macros
apr_file_info                 Properties of filesystem objects and paths
apr_file_io                    Filesystem I/O
apr_fnmatch                Filesystem pattern matching
apr_general                  Initialization/termination; useful macros
apr_getopt                   Command arguments
apr_global_mutex         Global locking routines
apr_hash                      Hash tables
apr_inherit                   File handle inheritance helpers
apr_lib                         Odds and ends
apr_mmap                   Memory mapping
apr_network_io            Network I/O (sockets)
apr_poll                       Poll routines
apr_pools                     Resource management
apr_portable                 APR to native mapping conversion
apr_proc_mutex           Process locking routines
apr_random                 Random numbers
apr_ring                       Ring data struct and macros
apr_shm                             Shared memory
apr_signal                    Signal handling
apr_strings                   String operations
apr_support                 Internal support function
apr_tables                    Table and array functions
apr_thread_cond           Thread conditions
apr_thread_mutex         Thread mutex routines
apr_thread_proc           Threads and process functions
apr_thread_rwlock        Reader/writer locking routines
apr_time                      Time/date functions
apr_user                             User and group ID services
apr_version                  APR version
apr_want                     Standard header support

表3-1
APR模块
名字                            功能
apr_allocator              用来内部内存分配
apr_atomic               原子操作
apr_dso                        动态加载 (.so/.dll)
apr_env                       读取和设置环境变量
apr_errno                     定义错误条件和宏
apr_file_info                 文件和路径的属性
apr_file_io                    文件系统 I/O
apr_fnmatch                文件系统格式匹配
apr_general                  初始化/结束; 有用的宏
apr_getopt                   命令行参数
apr_global_mutex         全局锁例程
apr_hash                      哈希表
apr_inherit                   File handle inheritance helpers
apr_lib                         零碎的东西
apr_mmap                   内存映射
apr_network_io            网络I/O (sockets)
apr_poll                       询问例程
apr_pools                     资源管理
apr_portable                 APR到本地映射的转换
apr_proc_mutex           进程锁例程
apr_random                 随机数
apr_ring                       令牌环数据结构和宏
apr_shm                             共享内存
apr_signal                    信号处理
apr_strings                   字符串操作
apr_support                 内部支持函数
apr_tables                    表和数组函数
apr_thread_cond           线程环境
apr_thread_mutex         线程互斥例程
apr_thread_proc           线程和进程函数
apr_thread_rwlock        读写锁例程
apr_time                      时间日期函数
apr_user                             用户和组ID服务
apr_version                  APR 版本
apr_want                     Standard header support

3.2 APR-UTIL
3.2 APR-UTIL

APR-UTIL (also known as APU) is a second library in the APR project. It provides
a small set of utilities, based on the APR and with a unified programming interface.
APU doesn’t have separate per-platform modules, but it does adopt a similar
approach to some other commonly used resources, such as databases.
Table 3-2 provides a complete list of APU modules.
APR-UTIL(也叫APU)是APR工程的第二个项目.它基于APR和统一的编程接口提供了小集合的实用功能.APU模块在每个平台是同一个,但是它采用了一个相似的方法,被作为其它一些使用的通用的资源.
表3-2提供了一个完整的APU模块列表:

TABLE 3-2
APU Modules
Name                          Purpose
apr_anylock                 Transparent any lock flavor wrapper
apr_base64                  Base-64 encoding
apr_buckets                 Buckets/bucket brigades
apr_date                             Date string parsing
apr_dbd                       Common API for SQL databases
apr_dbm                      Common API for DBM databases
apr_hooks                    Hook implementation macros
apr_ldap                             LDAP authentication APIs
apr_ldap_init                LDAP initialization APIs used mainly when initializing
secure connections to the LDAP server
apr_ldap_option            APIs for setting LDAP options
apr_ldap_url                 APIs for parsing and handling the LDAP URL
apr_md4                      MD4 encoding
apr_md5                      MD5 encoding
apr_optional                 Optional functions
apr_optional_hooks       Optional hooks
apr_queue                    Thread-safe FIFO queues
apr_reslist                    Pooled resources
apr_rmm                     Relocatable managed memory
apr_sdbm                    SDBM library
apr_sha1                      SHA1 encoding
apr_strmatch                String pattern matching
apr_uri                        URI parsing/construction
apr_uuid                      User identification
apr_xlate                      Charset conversion (I18N)
apr_xml                       XML parsing

表 3-2
APU 模块
名字                           功能
apr_anylock                 Transparent any lock flavor wrapper
apr_base64                  Base-64 编码
apr_buckets                 Buckets/bucket brigades
apr_date                             日期字符串解析
apr_dbd                       SQL 数据库通用API
apr_dbm                      Common API for DBM databases
apr_hooks                    拦截实现宏
apr_ldap                             LDAP 认证 API
apr_ldap_init                在初始和LDAP 服务器进行安全连接的时候主要使用的LDAP
初始化API
apr_ldap_option            设置LDAP 选项的API
apr_ldap_url                 解析处理LDAP URL的API
apr_md4                      MD4 编码
apr_md5                      MD5 编码
apr_optional                 可选函数
apr_optional_hooks       可选拦截
apr_queue                    线程安全FIFO 队列
apr_reslist                    缓冲池资源
apr_rmm                     可重定位的被管理资源
apr_sdbm                    SDBM 库
apr_sha1                      SHA1 编码
apr_strmatch                字符串样式匹配
apr_uri                        URI 解析/构造
apr_uuid                      用户识别
apr_xlate                      字符集转换 (I18N)
apr_xml                       XML解析


3.3 Basic Conventions
3.3 基本协定

APR and APR-UTIL adopt a number of conventions that give them a homogenous
API and make them easy to work with.
APR和APR-UTIL采用了一系列的协定,有着一致的API和是他们很容易被使用.

3.3.1 Reference Manual: API Documentation and Doxygen
3.3.1 参考手册:API文档和Doxygen

All of APR/APU is very well documented at the code level. Every public function
and data type is documented in the header file that defines it, in doxygen1-friendly
format. The header files themselves, or the doxygen-generated documentation, provide
a full API reference for programmers. If you have doxygen installed, you can
generate your own copy of the APR reference manual from the source code with the
command make dox.
所有的APR/APU有着很好的代码注释.每一个公共函数和数据类型在定义的头文件中有着注释,注释按照Doxygen格式.头文件自身或者Doxygen生成的文档为程序员提供了一个完整的API参考.如果你安装了Doxygen,能用它从源代码中生成一份自己的APR参考手册.

3.3.2 Namespacing
3.3.2 命名空间

All APR/APU public interfaces are prefixed with the string “apr_” (data types and
functions) or “APR_” (macros). This defines APR’s “reserved” namespace.
Within the APR namespace, most of the APR and APU modules use secondary
namespacing. This convention is often based on the name of the module in question.
For example, all functions in module apr_dbd are prefixed with the string
“apr_dbd_”. Sometimes an obviously descriptive secondary namespace is used. For

1. http://www.doxygen.org/

example, socket operations in module apr_network_io are prefixed with
“apr_socket_”.
所有的APR/APU公共接口有着”apr”(数据类型和函数)或”APR”(宏)的前缀.这个定义了APR的”保留”命名空间.在APR的命名空间里,大多数APR和APU模块使用第二级命名空间.这个协定常常基于模块名字.例如,apr_dbd模块中的所有函数有着”apr_dbd_”的前缀.有时使用有着明显说明的二级命名空间.例如, 操作socket 的模块有着apr_network_io前缀.

3.3.3 Declaration Macros
3.3.3宏的声明

Public functions in APR/APU are declared using macros such as APR_DECLARE,
APU_DECLARE, and APR_DECLARE_NONSTD. For example:
APR_DECLARE(apr_status_t) apr_initialize(void);
On most platforms, this is a null declaration and expands to
apr_status_t apr_initialize(void);
On platforms such as Windows with Visual C++, which require their own nonstandard
keywords such as _dllexport to enable other modules to use a function,
these macros will expand to the required keywords.
APR/APU中的公共函数使用宏来声明,例如APR_DECLARE, APU_DECLARE,和APR_DECLARE_NONSTD.例如:
       APR_DECLARE(apr_status_t) apr_initialize(void);
在大多数平台,这是一个空声明,展开如下
       apr_status_t apr_initialize(void);
在如windows平台的Visual C++,需要用他们自己的非标准关键字,如_dllexport,来让其他模块使用这个函数,这些宏会被替换为需要的关键字.

3.3.4 apr_status_t and Return Values
3.3.4 apr_status_t和返回值

A convention widely adopted in APR/APU is that functions return a status value
indicating success or an error code to the caller. The type is apr_status_t, which
takes integer values defined in apr_errno.h. Thus the usual prototype for an APR
function is
APR_DECLARE(apr_status_t) apr_do_something(...function args...);
Return values should routinely be tested, and error handling (recovery or graceful
failure) should be implemented. The return value APR_SUCCESS indicates success,
and we can commonly handle errors using constructs such as
apr_status_t rv;
...
rv = apr_do_something(... args ...);
if (rv != APR_SUCCESS) {
/* log an error */
return rv;
}
函数返回状态值这个协定在APR/APU中被广泛的采用,这个状态值用来告诉调用者是否成功或者错误代号.类型是apr_status_t,在apr_errno.h中定义为整型.因此常用的APR函数原型是
       APR_DECLARE(apr_status_t) apr_do_something(...function args...);
返回值应该在调用之后被检查,应该有错误处理(恢复或者优雅的报错). APR_SUCCESS返回值表示成功,我们能够用下面这个结构通用的处理错误
apr_status_t rv;
...
rv = apr_do_something(... args ...);
if (rv != APR_SUCCESS) {
/* log an error */
return rv;
}

Sometimes we may do more. For example, if do_something was a nonblocking I/O
operation and returned APR_EAGAIN, we will probably want to retry the operation.
Some functions return a string value (char* or const char*), a void*, or void.
These functions are assumed to have no failure conditions or to return a null
pointer on failure as appropriate.
有时候我们需要做更多的工作.例如,做的事情是非阻塞的I/O操作,返回一个APR_EAGAIN,这样我们可能希望在重试一次.有些函数返回字符串(char* 或者 const char*), void *, 或者void.这些函数被认为不会发生错误或者返回一个空指针表示失败.

3.3.5 Conditional Compilation
3.3.5 条件编译

By their very nature, a number of features of APR may not be supported on every
platform. For example, prior to version 5.x, FreeBSD had no native thread implementation
considered suitable for Apache; hence threads were not supported in
APR (unless the relevant options were set manually for compilation).
由于现实环境,一些APR的功能不可能在每一个平台都支持.例如, FreeBSD 5.x以前的版本不支持线程,为了适合Apache,因此APR中也不支持线程(除非在编译的时候手动修改相关的选项).

To enable applications to work around this issue, APR provides APR_HAS_* macros
for such features. When an application is concerned with such a feature, it should
use conditional compilation based on these macros. For example, a module performing
an operation that could lead to a race condition in a multithreaded environment
might want to use something like this:
为了让程序能在这种情况下工作,APR提供了APR_HAS_*宏来支持这个特性.当程序需要考虑这个特性的时候,应该用这些宏进行条件编译.例如,当一个模块在多线程环境下执行一个可能会导致竞争操作的时候,可能会像下面这样使用:

#if APR_HAS_THREADS
rv = apr_thread_mutex_lock(mutex);
if (rv != APR_SUCCESS) {
/* Log an error */
/* Abandon critical operation */
}
#endif
/* ... Execute critical section of code here ... */
#if APR_HAS_THREAD
apr_thread_mutex_unlock(mutex);
#endif

3.4 Resource Management: APR Pools
3.4 资源管理:APR池

The APR pools are a fundamental building block that lie at the heart of APR and
Apache; they serve as the basis for all resource management. The pools allocate
memory, either directly (in a malloc-like manner) or indirectly (e.g., in string
manipulation), and, crucially, ensure that memory is freed at the appropriate time.
But they extend much further, to ensure that other resources such as files or mutexes
can be allocated and will always be properly cleaned up. They can even deal with
resources managed opaquely by third-party libraries.

NOTE It is common practice in Apache to assume that pool
memory allocation never fails. The rationale for this assumption
is that if the allocation does fail, then the system is not recoverable,
and any error handling will fail, too.
APR池是位于APR和Apache心脏部位的基础构建部分;为所有的资源管理提供基本服务. APR池直接地(像malloc形式的)或者间接地(例如字符串的操作)分配内存,最重要地确保了在适当的时候释放内存.除此之外,它们还进一步的确保文件或互斥量这类资源的分配和正确的释放.它们甚至也能处理被第三方库不透明管理的资源.
       提示: 我们通常假设在Apache中内存分配不会失败.因为如果内存分配失败,系统已经是不稳定的了,错误处理也是无用的.

3.4.1 The Problem of Resource Management
3.4.1 资源管理的问题

Every programmer knows that when you allocate a resource, you must ensure that
it is released again when you’ve finished with it. For example:
char* buf = malloc(n) ;
... check buf is non null ...
... do something with buf ...
free(buf) ;
or
FILE* f = fopen(path, "r") ;
... check f is non null ...
... read from f ....
fclose(f) ;
每一个程序员都知道,你分配资源,当你完成相应的任务后应该再次把它释放.例如:
char* buf = malloc(n) ;
... check buf is non null ...
... do something with buf ...
free(buf) ;

FILE* f = fopen(path, "r") ;
... check f is non null ...
... read from f ....
fclose(f) ;

Clearly, failure to free buf or to close f is a bug. In the context of a long-lasting program
such as Apache, it would have serious consequences, up to and including
bringing the entire system down. Obviously, it is important to get resource management
right.
这是很清楚的,释放buf或者关闭句柄f失败是一个bug.在持久运行的程序里,像Apache,会有很很严重的后果,最终导致系统瘫痪.让资源管理器工作正确显得非常重要.

In trivial cases, this is straightforward. In a more complex case with multiple error
paths, in which even the scope of a resource is uncertain at the time it is allocated,
ensuring that cleanup takes place in every execution path is much more challenging.
In such circumstances, we need a better way to manage resources.
在简单情况下,这是简单明了的.在有着多个错误处理块的更复杂环境里面,甚至连资源分配代码的位置都不能确定,确保资源释放操作的被执行是非常有难度的.在这种情况下,我们需要一个好方式来管理资源.

Constructor/Destructor Model
构造/析构模式

One method of resource management is exemplified by the C++ concept of objects
having a constructor and a destructor. Many C++ programmers make the destructor
responsible for cleanup of all resources allocated by the object. This approach
works well provided all dynamic resources are clearly made the responsibility of an
object. But, as with the simple C approach, it requires a good deal of care and attention
to detail―for example, where resources are conditionally allocated or shared
between many different objects―and it is vulnerable to programming bugs.
资源管理的一种方法是模仿C++中的对象概念,有一个构造器和析构器.许多C++程序员用析构器来负责清除对象分配的资源.这种方法工作的很好, provided all dynamic resources are clearly made the responsibility of an object.但是,对简单的C语言,需要关注大量的细节一一例如,资源在哪儿有条件的分配或者不同对象间的共享一一这会使程序容易产生一些bug.

Garbage Collection Model
垃圾回收模式

A high-level method of resource management, typified by Lisp and Java, is garbage
collection. This approach has the advantage of taking the problem away from the
programmer and transferring it to the language itself, thereby completely removing
the danger of crippling programming errors. The drawback is that garbage collection
incurs a substantial overhead even where it isn’t necessary, and it deprives the
programmer of useful levels of control, such as the ability to control the lifetime of
a resource. It also requires that all program components―including third-party
libraries―be built on the same system, which is clearly not possible in an open system
written in C.
一种更高级别的资源管理方法是垃圾回收机制,Lisp和Java是其代表.这种方法的好处是让程序员远离这些麻烦,把麻烦交给语言自身.因此完全规避了这种难查错误的编程错误风险.缺点是这种垃圾回收机制带来了一些不必要的额外负担,剥夺了程序员的一些有用控制,像控制一个资源的生命周期.它也需要所有的编程组件一一包括第三方库文件一一在相同系统上构建,在一个用C语言编写的开放系统中这是明显不可能的.

3.4.2 APR Pools
3.4.2 APR池

The APR pools provide an alternative model for resource management. Like
garbage collection, they liberate the programmer from the complexities of dealing
with cleanups in all possible cases. In addition, they offer several other advantages,
including full control over the lifetime of resources and the ability to manage heterogeneous
resources.
APR池为资源管理提供了一个可供选择的模式,像垃圾回收,它们把程序员从处理所有可能情况下的资源释放的复杂问题中解放了出来.除此之外,它们还提供了一些其他的好处,包括可以完成控制资源的生命周期和有能力去管理不同类的资源.

The basic concept goes like this: Whenever you allocate a resource that requires
cleanup, you register it with a pool. The pool then takes responsibility for the
cleanup, which will happen when the pool itself is cleaned. In this way, the problem
is reduced to one of allocating and cleaning up a single resource: the pool itself.
Given that the Apache pools are managed by the server itself, the complexity is,
therefore, removed from applications programming. All the programmer has to do
is select the appropriate pool for the required lifetime of a resource.
基本概念像这样:无论何时你分配一个需要被释放的资源,你注册它为pool类型,在这个资源被销毁的时候,pool会负责进行清除操作.在这种方式下,问题是分配和清除一个单一资源是pool自身了.假设Apache的pool由服务器自身管理,因此,应用编程中的复杂问题没有了.所有的程序员必须根据资源的生命周期去选择一个适当的pool.

Basic Memory Management
基本内存管理

The most basic application of pools is for memory management. Instead of
mytype* myvar = malloc(sizeof(mytype)) ;
/* make sure it gets freed later in every possible execution path */
we use
mytype* myvar = apr_palloc(pool, sizeof(mytype)) ;
最基本的pool应用是内存管理.取代
mytype* myvar = malloc(sizeof(mytype)) ;
/* 在每一可能的执行路径中确保它被释放 */
我们使用
mytype* myvar = apr_palloc(pool, sizeof(mytype)) ;

The pool automatically takes responsibility for freeing this resource, regardless of
what may happen in the meantime. A secondary benefit is that pool allocation is
faster than malloc on most platforms!
pool自动的负责资源的释放,不用考虑与此同时发生了什么.第二个好处是在大多数平台上pool的分配要比malloc快!

Basic memory management takes many forms in APR and Apache, where memory
is allocated within another function. Examples include string-manipulation functions
and logging, where we gain the immediate benefit of being able to use
constructs such as the APR version of sprintf() without having to know the size
of a string in advance:
char* result = apr_psprintf(pool, fmt, ...) ;
APR also provides higher-level abstractions of pool memory―for example, in the
buckets used to pass data down the filter chain.

在APR和Apache中基本内存管理有多种形式,分配内存发生在其他函数里.如字符串处理函数和日志,我们能从使用构造中获得立即的好处,像APR版本的sprintf(),不需要预先知道字符串的大小.
       char* result = apr_psprintf(pool, fmt, ...) ;
APR也提供了更高级别抽象的pool内存一一例如,在桶中传递数据到过滤链.

Generalized Memory Management
一般化的内存管理

APR provides built-in functions for managing memory, as well as a few other basic
resources such as files, sockets, and mutexes. However, programmers are not
required to use these functions and resources. An alternative is to use native allocation
functions and explicitly register a cleanup with the pool:
mytype* myvar = malloc(sizeof(mytype)) ;
apr_pool_cleanup_register(pool, myvar, free,
apr_pool_cleanup_null) ;
or
FILE* f = fopen(filename, "r") ;
apr_pool_cleanup_register(pool, f, fclose, apr_pool_cleanup_null) ;
APR为内存管理提供了内建的函数,也包括其它的基本资源如文件,套接字和互斥量.可是,程序员不需要使用这些函数和资源.我们可以选择使用本地系统分配函数和明确的在pool上注册一个清除操作:
mytype* myvar = malloc(sizeof(mytype)) ;
apr_pool_cleanup_register(pool, myvar, free,
apr_pool_cleanup_null) ;
或者
FILE* f = fopen(filename, "r") ;
apr_pool_cleanup_register(pool, f, fclose, apr_pool_cleanup_null) ;

This code delegates responsibility for cleanup to the pool, so that no further action
from the programmer is required. However, native functions may be less portable
than the APR equivalents from apr_pools and apr_file_io, respectively, and
malloc on most systems will be slower than using the pool.
这个代码代表了pool的清除职责,因此不需要程序员的更进一步的处理.但是本地系统函数比APR中等价的apr_pools和apr_file_io缺少移植性,在大多数系统上malloc要比使用pool慢.

This method of memory management generalizes to resources opaque to Apache
and APR. For example, to open a MySQL database connection and ensure it is
closed after use, you would write the following code:
MYSQL* sql = NULL ;
sql = mysql_init(sql) ;
if ( sql == NULL ) { log error and return failure ; }
apr_pool_cleanup_register(pool, sql, mysql_close,
apr_pool_cleanup_null) ;
sql = mysql_real_connect(sql, host, user, pass,
dbname, port, sock, 0) ;
if ( sql == NULL ) { log error and return failure ; }
这种内存管理方法使得资源对Apache和APR不透明.例如,打开MySQL数据库,确保在使用后被关闭,你可能会编写如下代码:
MYSQL* sql = NULL ;
sql = mysql_init(sql) ;
if ( sql == NULL ) { log error and return failure ; }
apr_pool_cleanup_register(pool, sql, mysql_close,
apr_pool_cleanup_null) ;
sql = mysql_real_connect(sql, host, user, pass,
dbname, port, sock, 0) ;
if ( sql == NULL ) { log error and return failure ; }

Note that apr_dbd (which is discussed in Chapter 11) provides an altogether better
method for managing database connections.
提示:apr_dbd(第11章讨论)为管理数据库连接提供了一个更好的方法.

As a second example, consider XML processing:
xmlDocPtr doc = xmlReadFile(filename);
apr_pool_cleanup_register(pool, doc, xmlFreeDoc,
apr_pool_cleanup_null) ;
/* Now we do things with doc, which may allocate further memory
* managed by the XML library but will be cleaned by xmlFreeDoc
*/
Integrating C++ destructor-cleanup code provides yet another example. Suppose
we have
class myclass {
public:
virtual ~myclass() { do cleanup ; }
// ....
} ;
We define a C wrapper:
void myclassCleanup(void* ptr) { delete (myclass*)ptr ; }
We then register this wrapper with the pool when we allocate myclass:
myclass* myobj = new myclass(...) ;
apr_pool_cleanup_register(pool, (void*)myobj, myclassCleanup,
apr_pool_cleanup_null) ;
// Now we've hooked our existing resource management from C++
// into Apache and never need to delete myobj;
// pool cleanup will do the job for us
第二个例子,考虑XML的处理:
xmlDocPtr doc = xmlReadFile(filename);
apr_pool_cleanup_register(pool, doc, xmlFreeDoc,
apr_pool_cleanup_null) ;
/* 现在开始使用 doc, XML库可能还会在doc中分配内存,但是都会被 xmlFreeDoc释放
*/
另一个例子整合了C++的析构函数.假设我们这样
class myclass {
public:
virtual ~myclass() { do cleanup ; }
// ....
} ;
定义一个C wrapper:
void myclassCleanup(void* ptr) { delete (myclass*)ptr ; }
当我们分配myclass的时候,向pool注册这个wrapper:
myclass* myobj = new myclass(...) ;
apr_pool_cleanup_register(pool, (void*)myobj, myclassCleanup,
apr_pool_cleanup_null) ;
//现在我们用Apache接管了原先的C++资源管理,在不需要去调用delete myobj;pool会
//为我们做这个工作


Implicit and Explicit Cleanup
内在的和清楚的清除操作

Suppose we want to free our resource explicitly before the end of the request―for
example, because we’re doing something memory intensive but have objects we can
free. We may want to do everything according to normal scoping rules and just use
pool-based cleanup as a fallback to deal with error paths. However, because we registered
the cleanup, it will run regardless of our intentions. In the worst-case scenario,
it could possibly lead to a double-free and a segfault.
我们想在处理请求结束之前就明确的释放我们的资源一一例如,因为我们在做及其需要内存的操作但是又有对象能被释放.我们可能想根据正常范围规则来做事情, 在错误处理通路上仅仅使用基于pool清除作为一个可靠的处理.可是,因为我们注册了清除操作,它的运行操作不会考虑我们的目的.在最坏的情况下,这个可能导致二次释放和段错误.

Another pool function, apr_pool_cleanup_kill, is provided to deal with this
situation. When we run the explicit cleanup, we unregister the cleanup from the
pool. Of course, we can be a little more clever about how we go about this task.
另一个pool函数apr_pool_cleanup_kill,为这种情况提供了处理.当我们运行明确的清除操作的时候,我们从pool中反注册清除操作.当然,我们也能够更灵活的处理这种情况.

Here’s the outline of a C++ class that manages itself based on a pool, regardless of
whether it is explicitly deleted:
这里有基于pool的C++类资源管理的原则,不用明确的考虑是否被delete:
class poolclass {
private:
apr_pool_t* pool ;
public:
poolclass(apr_pool_t* p) : pool(p) {
apr_pool_cleanup_register(pool, (void*)this,
myclassCleanup, apr_pool_cleanup_null) ;
}
virtual ~poolclass() {
apr_pool_cleanup_kill(pool, (void*)this, myclassCleanup) ;
}
} ;

If you use C++ with Apache (or APR), you can derive any class from poolclass.
Most APR functions do something equivalent to this, conducting register and kill
operations whenever resources are allocated or cleaned up.
如果你在Apache(或者APR)中使用C++,你能够从poolclass派生任何类.大多数APR函数做的事情和这个一样,管理注册和解除注册操作,无论资源何时被分配或者清除.

In simple C, we would use the following generic form:
在简单C语言中,我们能使用下面这种通用形式:

/* Allocate something */
my_type* my_res = my_res_alloc(args) ;
/* Handle errors */
if (my_res == NULL) {
/* Log error and bail out */
}
/* Ensure it won't leak by registering a cleanup */
apr_pool_cleanup_register(pool, my_res,
my_res_free, apr_pool_cleanup_null) ;
/* ... Now use it as required ... */
/* OK, we're done with it, and we'd like to release it ASAP */
rv = my_res_free(my_res) ;
/* Since we freed it, we want to kill the cleanup */
apr_pool_cleanup_kill(pool, my_res, my_res_free) ;
/* Now handle errors and continue */
if (rv != APR_SUCCESS) { /* or whatever test may be appropriate */
/* ... Log error and bail out or attempt recovery ... */
}

We can also streamline this form by running the cleanup and unregistering it with
the pool using a single function:
apr_pool_cleanup_run(pool, my_res, my_res_free) ;
我们也可以简化这种形式,在pool上运行清除和反注册使用一个单一的函数:
apr_pool_cleanup_run(pool, my_res, my_res_free) ;

3.4.3 Resource Lifetime
3.4.3 资源生命周期

When we allocate resources by using a pool, we automatically ensure that they get
cleaned up at some point. But when? We need to make sure the cleanup happens at
the right time―that is, neither while the resource is still in use, nor long after the
resource is no longer required.
当我们使用pool分配资源的时候,在某种程度上能自动确保资源被释放.但是何时?我们需要确定在正确的时间执行清除操作一一既不能在资源还在被使用,也不能在资源长期不被需要之后.

Apache Pools
Apache Pools

Fortunately, Apache makes this process quite easy, by providing different pools for
different types of resource. These pools are associated with relevant structures of the
httpd, and they have the lifetime of the corresponding struct. Four general-purpose
pools are always available in Apache:
• The request pool, with the lifetime of an HTTP request
• The process pool, with the lifetime of a server process
• The connection pool, with the lifetime of a TCP connection
• The configuration pool
幸运地,Apache十分容易的确保这个过程,通过为不同类型的资源提供不同pools.这些pools绑定在httpd的相关结构,它们有着相应结构的生命周期.在Apache中总是可用的四个一般目的的pools:
• 请求pool,一个HTTP请求的生命周期
• 进程pool,服务器进程的生命周期
• 连接pool,一次TCP连接的生命周期
• 配置pool

The first three, which are associated with the relevant Apache structs, are accessed
as request->pool, connection->pool, and process->pool, respectively. The
fourth, process->pconf, is also associated with the process, but differs from the
process pool in that it is cleared whenever Apache rereads its configuration.
头三个绑定在Apache的相关结构,能各自通过request->pool, connection->pool, and process->pool访问.第四个, process->pconf,绑定在进程上,和进程pool不同的是在Apache读取它的配置的时候被清除.

The process pool is suitable for long-lived resources, such as those that are initialized
at server start-up. The request pool is suitable for transient resources used to
process a single request.
进程pool适合长期存在的资源,例如在服务器初始化的时候被初始化的资源.请求pool适合处理一个请求的短暂资源.

The connection pool has the lifetime of a connection, which normally consists of
one or more requests. This pool is useful for transient resources that cannot be associated
with a request―most notably, in a connection-level filter, where the
request_rec structure is undefined, or in a non-HTTP protocol handler.
连接pool有着一次连接的生命周期,一次连接包括一个或者更多请求.对不只在一个请求有效的短暂资源是有用的一一在没有request_rec数据的连接层过滤器中是特别显著的或者一个非HTTP协议的处理器中.

In addition to these standard pools, special-purpose pools may be created for other
purposes, such as configuration and logging, or may be created privately by modules
for their own use.
除这些标准的pools之外,一些特别其他目的的pools也可以被创建,例如配置和日志,或者为模块自己使用的,也能被创建.

Using Pools in Apache: Processing a Request
在Apache中使用Pools:处理一个请求

All request-processing hooks take the form
所有请求处理拦截像这样

int my_func(request_rec* r) {
/* implement the request processing hook here */
}

This hook puts the request pool r->pool at our disposal. As discussed earlier, the
request pool is appropriate for the vast majority of operations involved in processing
a request. We pass it to Apache and APR functions that need a pool argument
as well as our own.
这个拦截传入请求pool r->pool由我们支配,已经在前面谈到,请求pool适合在处理一个请求过程中的大量操作.我们为需要pool参数的Apache和APR函数也为我们自己传入这个参数.

The process pool is available as r->server->process->pool for operations that
need to allocate long-lived resources―for example, caching a resource that should
be computed once and subsequently reused in other requests. However, this process
is a little more complex, and it is generally preferable to derive a subpool from the
process pool, as discussed in Chapters 4 and 10.
The connection pool is r->connection->pool.
通过r->server->process->pool能得到进程pool,对那些需要分配长久资源的操作,进程pool是有效的一一例如,缓存一个资源,应该只被计算一次,能被接下来其他的请求重用.可是,这个过程是有一点复杂的,一般更好的办法是从进程pool继存一个子pool,在第4章和第10章中讨论.
连接pool是r->connection->pool

Using Pools in Apache: Initialization and Configuration
在Apache中使用Pools:初始化和配置

The internal workings of Apache’s initialization are complex. As far as modules are
concerned, however, the initialization can normally be treated as a simple procedure:
Just set up a configuration, and everything is permanent. Apache makes that
easy, because most of the relevant hooks have prototypes that pass the relevant pool
as their first argument.
Apache初始化的内部工作是复杂的.可是作为模块关心的,初始化能被简单的看成一个仅仅设置配置的过程,每件事情都是一层不变的.Apache让这个变的容易,因为大多数相关的拦截已经有原型,把相关的pool作为第一个参数传入.

Configuration Handlers
配置处理器

static const char* my_cfg(cmd_parms* cmd, void* cfg, /* args */ )
Use the configuration pool, cmd->pool, to give a configuration the lifetime of the
directive.
static const char* my_cfg(cmd_parms* cmd, void* cfg, /* args */ )
使用配置pool,cmd->pool,来给配置有着指令的生命周期.

Pre-configuration and Post-configuration Hooks
配置前和配置后的拦截

These hooks are unusual in having several pools passed:
static int my_pre_config(apr_pool_t* pool,
apr_pool_t* plog, apr_pool_t* ptemp)
For most purposes, just use the first pool argument. ptemp is suitable for resources
used during configuration, but will be destroyed before Apache goes into operational
mode. plog remains active for the lifetime of the server, but is cleaned up each time
the configuration is read.
传入多个pools参数的这些拦截不常见:
static int my_pre_config(apr_pool_t* pool,
apr_pool_t* plog, apr_pool_t* ptemp)
对大多数情况,仅仅使用第一个pool参数就够了. ptemp适合配置阶段的资源使用,但是在Apache进入运行模式之前会被销毁. plog在整个服务的阶段都是有效的,但是在每一次配置被读取的时候被清除.

Child init
子pool的初始化

static void my_child_init(apr_pool_t* pool, server_rec* s).
The child pool is the first argument.
static void my_child_init(apr_pool_t* pool, server_rec* s).
第一个参数是子pool.

Monitor
监视器

static int my_monitor(apr_pool_t* pool)
The monitor is a special case: It runs in the parent process and is not tied to any
time-limited structure. For this reason, resources allocated in a monitor function
should be explicitly freed. If necessary, a monitor may create and free its own subpool
and manage it as discussed in Chapter 4. Few applications will need to use the
monitor hook.
static int my_monitor(apr_pool_t* pool)
监视器是一个特别的例子:它运行在父进程中,不依赖任何有时效性的结构.因为这个,在监视器函数中分配的资源应该被显示释放.如果可能,监视器可以创建和释放自己的子pool,然后管理这个子pool,在第4章中讨论.很少有程序会需要使用这个监视器拦截.

Using Pools in Apache: Other Cases
在Apache中使用Pools:其他情况

Most Apache modules involve the initialization and request processing we have
already discussed. There are two other cases to deal with, however: connection functions
and filter functions.
大多数Apache模块中涉及的初始化和请求处理过程我们已经讨论过了.可是,这里有两个其它情况需要处理:链接函数和过滤函数

Connection Functions
连接函数

The pre_connection and process_connection connection-level hooks pass a
conn_rec object as their first argument; they are directly analogous to request functions
as far as pool resources are concerned. The create_connection connectioninitialization
hook passes the pool as its first argument. Any module implementing
this hook takes responsibility for setting up the connection.
连接前和连接处理中的连接层拦截传入了一个conn_rec对象作为他们的第一个参数;至于关心的pool资源,它们和请求函数是直接相似的.创建连接时的连接初始化拦截以第一个参数传递pool.任何实现这个拦截的模块负责连接的设置.

Filter Functions
过滤器函数

Filter functions receive an ap_filter_t as their first argument. This object
ambiguously contains both a request_rec and a conn_rec as members, regardless
of whether it is a request-level or a connection-level filter. Content filters should
normally use the request pool. Connection-level filters will get a junk pointer in
f->r (the request doesn’t exist outside the protocol layer; see Chapter 8) and must
use the connection pool. Be careful: This can be a trap for the unwary.
过滤器函数接受ap_filter_t作为第一个参数.这个对象模糊地包含request_rec和conn_rec成员,很少考虑是否是请求级别或者连接级别的过滤器.内容过滤器适应正常地使用请求pool.连接级别的过滤器将会得到一个垃圾指针f->r(超出协议层,请求不存在,见第8章),必须使用连接pool.当心:不留意就会掉入这个陷进.

3.4.4 Limitations of Pools
3.4.4 Pools的限制

So far, we have seen the advantages of using pools for resource management.
Naturally, there are also some limitations:
• Managing resources that have a lifetime that doesn’t correspond to any of
Apache’s main objects requires more work. This issue is discussed further in
Chapter 4.
• Allocating resources from a pool is not thread safe. This is rarely an issue,
because most pool allocation by modules when Apache is running on a multithreaded
basis uses a pool owned by an object (HTTP request or TCP connection)
that is thread private at the time of use. Chapter 4 discusses some
cases where thread safety is an issue.
• APR pools never return memory to the operating system until they are
destroyed (they do, of course, reuse memory, so pool-based applications don’t
grow indefinitely). Thus it may sometimes make sense to use malloc rather
than pools when allocating very large blocks of memory. Conversely, using
malloc in your code may affect binary compatibility. On Windows, it may
prevent your code from being linked with a binary compiled using a different
version of Visual C++, due to incompatibilities in the runtime libraries.
到目前为止,我们已经看到使用pools管理资源的好处.实际中,这里也有一些限制:
• 管理生命周期和Apache主要对象不一直的资源需要更多工作.在第4章中进一步讨论这个情况.
• 从pool分配的资源不是线程安全的.在很少情况下会有问题,因为当Apache运行在一个多线程环境下,在模块大多数pool分配的操作中,基本只有一个对象(HTTP请求或者TCP连接)使用pool,也只在一个线程内部.第4章讨论一些有线程安全问题的一些情况.
• APR pools从不释放内存给操作系统直到它们被销毁(当然,他们这样做,让基于pool的应用程序不会无限制的消耗内存).因此在分配非常大块的内存的时候,我们使用malloc要比pool好.相反,在你的代码中使用malloc可能会影响二进制文件的兼容性.在Windows上,由于不兼容的运行期库,这个可以阻止你的代码被链接到使用不同Visual C++版本编译的二进制文件上

作者: 蟋蟀   发布时间: 2007-08-01

面介绍一下怎样打开apache的mod_rewrite功能。


首先来重新编译安装apache-2.0.59

#tar -zxvf httpd-2.0.59.tar.gz
#cd httpd-2.0.59
#./configure �Cprefix=/opt/apache2 �Cenable-modules=so �Cenable-module=rewrite �Cenable-shared=rewrite �Cenable-shared=max
#make
#make install

apache安装完毕,下面来加载编译mod_rewrite.so这个模块。

#cd /soft/httpd-2.0.59/modules/mappers
#/opt/apache2/bin/apxs -c mod_rewrite.c -lgdbm
#gcc -shared -o mod_rewrite.so mod_rewrite.o -lgdbm
#/opt/apache2/bin/apxs -i -A -n rewrite mod_rewrite.so





最近,项目需要在已经编译好的Apache上以动态方式加载rewrite模块。于是,我们在网上找了许多资料,但都讲解得不是很详细且格式错位问题严重。所以我们有必要把这一过程再重述一遍。实际操作步骤如下:

工作现场描述:
    Linux 2.4.21
    apache_1.3.34.tar.gz    解压后的目录为 /root/apache_1.3.34 文中简称为“源码目录”
    apache安装目录 /usr/local/apache 文中简称为“目标目录”

步骤:
1、Apache安装rewrite模块的时候需要DBM支持,否则无法编译,所以首先要安装一个GDBM  
    下载地址:ftp://ftp.gnu.org/gnu/gdbm/
    安装步骤:
        进入安装目录,
        ./configure
        make
        make install
        make install-compat    (最后行也要执行。否则无法编译出ndbm.h头文件)
    如果您不能确定服务器上是否已经装有DBM,那么可以安装一下。否则这步可以跳过。

2、现在到apache源码目录的标准模块目录中(/root/apache_src/src/modules/standard/)中,使用如下指令编译出so文件:
    /usr/local/apache/bin/apxs -c mod_rewrite.c -lgdbm
  即可得到mod_rewrite.so文件。
    备注:"-lgdbm"是用为说明在编译mod_rewrite.c时要把gdbm链接进来。这样在第6步启动apache时就不会报出"dbm fetch"的错误了。

3、现在让apache的apxs来自动向http.conf配置文件中加入LoadModule语句并将mod_rewrite.so文件拷贝到apache/libexec目录
    /usr/local/apache/bin/apxs -i -A -n rewrite /root/apache_1.3.34/src/modules/standard/mod_rewrite.so
    备注:命令中的rewrite参数是告诉apxs命令mod_rewrite.so文件中的模块名。在命令执行后,apxs会在LoadModule中为rewrite加上"_module"以标名模块名称。如果你在启动apache时发现总是给出“不能定位API”之类的错误,那就是说明LoadModule后面的模块名的语法要根据您的apache版本加以改变。

4、停止apache
    apache/bin/apachectl stop

5、运行配置文件检查命令
    apache/bin/apachectl configtest
    如显示Syntax OK,则表示整个操作成功。那么可以到第6步。否则根据提示信息进行调试。但只要按照此文所说进行操作是不会出错的。

6、启动apache
    apache/bin/apachectl start










现在查看phpinfo的信息


Loaded Modules   mod_dir mod_imap mod_actions mod_userdir mod_alias mod_rewrite mod_so sapi_apache2  


表明我的机器就支持rewrite了,但是我的dz怎么不能静态呢?
要修改以下配置
1.在配置文件(通常就是 conf/httpd.conf)中加入如下代码
<IfModule mod_rewrite.c>
                RewriteEngine On
                RewriteRule ^(.*)/archiver/([a-z0-9\-]+\.html)$ $1/archiver/index.php?$2
                RewriteRule ^(.*)/forum-([0-9]+)-([0-9]+)\.html$ $1/forumdisplay.php?fid=$2&page=$3
                RewriteRule ^(.*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/viewthread.php?tid=$2&extra=page\%3D$4&page=$3
                RewriteRule ^(.*)/profile-(username|uid)-(.+)\.html$ $1/viewpro.php?$2=$3
        </IfModule>
RewriteEngine On
RewriteRule ^archiver/([a-z0-9\-]+\.html)$ archiver/index.php?$1
RewriteRule ^forum-([0-9]+)-([0-9]+)\.html$ forumdisplay.php?fid=$1&page=$2
RewriteRule ^thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ viewthread.php?tid=$1&extra=page\%3D$3&page=$2
RewriteRule ^profile-(username|uid)-(.+)\.html$ viewpro.php?$1=$2

保存。
2.检查论坛所在目录中是否存在 .htaccess 文件,如果不存在,请手工建立此文件。文件内容为:

RewriteEngine On
# 修改以下语句中的 /discuz 为你的论坛目录地址,如果程序放在根目录中,请将 /discuz 修改为 /
        RewriteBase /

        RewriteRule ^archiver/([a-z0-9\-]+\.html)$ archiver/index.php?$1
        RewriteRule ^forum-([0-9]+)-([0-9]+)\.html$ forumdisplay.php?fid=$1&page=$2
        RewriteRule ^thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ viewthread.php?tid=$1&extra=page\%3D$3&page=$2
        RewriteRule ^profile-(username|uid)-(.+)\.html$ viewpro.php?$1=$2




保存,重新启动。。。

作者: 蟋蟀   发布时间: 2007-08-01

Apache模块开发/用C语言扩展apache(1:简述)
by linux_prog
   Apache是一个非常稳定而且非常open的web server,它的很多功能都可以通过plugin的方式去扩展。
比如:mod_proxy使得apache可以作代理, mod_rewrite使得apache可以实现非常强大的url mapping和rewritting
功能,你是否也想自己来开发一个apache module呢?网上这方面的文章非常的少,而且全是E文,
希望我的这篇文章能够给你一些实质性的帮助。
   开发apache module之前,我们有必要先分析一下其源代码。
   $ cd httpd-2.2.4/
   $ ls
   其中:server/目录是apache核心程序的代码
         include/目录存放主要的头文件
         srclib/目录存放apr和apr-util代码(这两个是什么,后面介绍)
         modules/目录下存放目前已经有的各种module(可以看看这些代码先)
  
   $ cd include/
   先分析一下apache的头文件
   $ vi httpd.h
   第766行,这个结构非常的重要,后面编写模块时都要用到这个结构,所以分析一下。
   每个http request都会对应这个结构的一个实例。
   由于apache的源代码都有很详细的英文注释,所以我也不翻译了。
struct request_rec {
    /** The pool associated with the request */
    //内存管理池,后面讲apr时会讲到
    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 (eg. GET, HEAD, POST, etc.) */
    const char *method;
    /** M_GET, M_POST, etc. */
    int method_number;

    /**
     *  ‘allowed’ is a bitvector 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
     * (eg. 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 sub processes */
    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 can not 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;
    /* XXX: What does this mean? Please define “canonicalize” -aaron */
    /** 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 64bit alignment the next time we need to break
* binary compatibility for some other reason.
*/
};
   可以看到源码中有很多apr_开头的结构,这个是什么呢?下节介绍一下。

作者: 蟋蟀   发布时间: 2007-08-01

from: http://www.loveopensource.com/?p=17

Apache模块开发/用C语言扩展apache(2:APR编程介绍)
by linux_prog
    可以看到apache代码中使用了大量的以apr_开头的结构或者函数,这些其实是APR.
    什么是apr?
    我的理解是apache工作小组在编写apache等C程序过程中所积累的一套编程框架,里面提供比较先进的内存管理模式
和常用的数据结构,另外根据各种平台作了一些不同的宏定义,让代码做到平台无关性。由于做得不错,后来,就干脆把它从apache源代码中脱离出来,又搞了一个项目,apache官方站点上也有它的相关介绍:http://apr.apache.org/
    The mission of the Apache Portable Runtime (APR) project is to create and maintain software libraries that provide a predictable and consistent interface to underlying platform-specific implementations. The primary goal is to provide an API to which software developers may code and be assured of predictable if not identical behaviour regardless of the platform on which their software is built, relieving them of the need to code special-case conditions to work around or take advantage of platform-specific deficiencies or features.
     apr可以独立于apache安装,让我们编写应用程序时也使用。
     有个非常好的英文文档介绍了怎么样使用:http://dev.ariel-networks.com/ap ... l/apr-tutorial.html
     上面那个文档非常不错,大家看懂了,我写的这篇就不用看了。
   
     我这个把几个以后经常要用到的介绍一下。

    (1) apr_pool_t *pool;
    内存池,所有的内存操作可以基于这个池之上,当这个池不再需要时,一次性释放该池上的所有内存,这个模式非常适合基于
session服务的编程模式,比如:每个http request都分配一个apr_pool_t,当该http request end时,一次性释放该request运行时申请的所有内存。可以看到struct request_rec中就有这个东东。
    用法:
apr_pool_t *mp;
/* create a memory pool. */
apr_pool_create(&mp, NULL);

/* allocate memory chunks from the memory pool */
char *buf1;
buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);
/* sample code about apr_pool_clear() */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
for (i = 0; i < n; ++i) {
    do_operation(..., mp);
    apr_pool_clear(mp);
}
apr_pool_destroy(mp);

    (2)数据结构--容器
    动态数组---apr_array_header_t
    类似于hashmap的apr_table_t:
    apr_table_t *tab;
    tab = apr_table_make(mp, 5);
    apr_table_setn(tab, "foo", "bar"); /* string literals don't require duplication */
    apr_table_setn(tab, apr_pstrdup(mp, "foo"), apr_pstrdup(mp, "bar"));
    const char *v = apr_table_get(tab, "mykey");
   
    可以看到apr_table_t存储key-value pairs类型的数据非常方便,难怪apache所有的http header都用它存储。
    其他操作apr_table_t的函数:
/**
* Get the elements from a table
* @param t The table
* @return An array containing the contents of the table
*/
APR_DECLARE(const apr_array_header_t *) apr_table_elts(const apr_table_t *t);

/**
* Determine if the table is empty
* @param t The table to check
* @return True if empty, False otherwise
*/
APR_DECLARE(int) apr_is_empty_table(const apr_table_t *t);

/**
* Determine if the array is empty
* @param a The array to check
* @return True if empty, False otherwise
*/
APR_DECLARE(int) apr_is_empty_array(const apr_array_header_t *a);

/**
* Create an array
* @param p The pool to allocate the memory out of
* @param nelts the number of elements in the initial array
* @param elt_size The size of each element in the array.
* @return The new array
*/
APR_DECLARE(apr_array_header_t *) apr_array_make(apr_pool_t *p,
                                                 int nelts, int elt_size);

/**
* Add a new element to an array (as a first-in, last-out stack)
* @param arr The array to add an element to.
* @return Location for the new element in the array.
* @remark If there are no free spots in the array, then this function will
*         allocate new space for the new element.
*/
APR_DECLARE(void *) apr_array_push(apr_array_header_t *arr);

/**
* Remove an element from an array (as a first-in, last-out stack)
* @param arr The array to remove an element from.
* @return Location of the element in the array.
* @remark If there are no elements in the array, NULL is returned.
*/
APR_DECLARE(void *) apr_array_pop(apr_array_header_t *arr);

/**
* Concatenate two arrays together
* @param dst The destination array, and the one to go first in the combined
*            array
* @param src The source array to add to the destination array
*/
APR_DECLARE(void) apr_array_cat(apr_array_header_t *dst,
           const apr_array_header_t *src);

/**
* Copy the entire array
* @param p The pool to allocate the copy of the array out of
* @param arr The array to copy
* @return An exact copy of the array passed in
* @remark The alternate apr_array_copy_hdr copies only the header, and arranges
*         for the elements to be copied if (and only if) the code subsequently
*         does a push or arraycat.
*/
APR_DECLARE(apr_array_header_t *) apr_array_copy(apr_pool_t *p,
                                      const apr_array_header_t *arr);
/**
* Copy the headers of the array, and arrange for the elements to be copied if
* and only if the code subsequently does a push or arraycat.
* @param p The pool to allocate the copy of the array out of
* @param arr The array to copy
* @return An exact copy of the array passed in
* @remark The alternate apr_array_copy copies the *entire* array.
*/
APR_DECLARE(apr_array_header_t *) apr_array_copy_hdr(apr_pool_t *p,
                                      const apr_array_header_t *arr);

/**
* Append one array to the end of another, creating a new array in the process.
* @param p The pool to allocate the new array out of
* @param first The array to put first in the new array.
* @param second The array to put second in the new array.
* @return A new array containing the data from the two arrays passed in.
*/
APR_DECLARE(apr_array_header_t *) apr_array_append(apr_pool_t *p,
                                      const apr_array_header_t *first,
                                      const apr_array_header_t *second);

/**
* Generates a new string from the apr_pool_t containing the concatenated
* sequence of substrings referenced as elements within the array.  The string
* will be empty if all substrings are empty or null, or if there are no
* elements in the array.  If sep is non-NUL, it will be inserted between
* elements as a separator.
* @param p The pool to allocate the string out of
* @param arr The array to generate the string from
* @param sep The separator to use
* @return A string containing all of the data in the array.
*/
APR_DECLARE(char *) apr_array_pstrcat(apr_pool_t *p,
          const apr_array_header_t *arr,
          const char sep);

/**
* Make a new table
* @param p The pool to allocate the pool out of
* @param nelts The number of elements in the initial table.
* @return The new table.
* @warning This table can only store text data
*/
APR_DECLARE(apr_table_t *) apr_table_make(apr_pool_t *p, int nelts);

/**
* Create a new table and copy another table into it
* @param p The pool to allocate the new table out of
* @param t The table to copy
* @return A copy of the table passed in
*/
APR_DECLARE(apr_table_t *) apr_table_copy(apr_pool_t *p,
                                          const apr_table_t *t);

/**
* Delete all of the elements from a table
* @param t The table to clear
*/
APR_DECLARE(void) apr_table_clear(apr_table_t *t);

/**
* Get the value associated with a given key from the table.  After this call,
* The data is still in the table
* @param t The table to search for the key
* @param key The key to search for
* @return The value associated with the key, or NULL if the key does not exist.
*/
APR_DECLARE(const char *) apr_table_get(const apr_table_t *t, const char *key);

/**
* Add a key/value pair to a table, if another element already exists with the
* same key, this will over-write the old data.
* @param t The table to add the data to.
* @param key The key fo use
* @param val The value to add
* @remark When adding data, this function makes a copy of both the key and the
*         value.
*/
APR_DECLARE(void) apr_table_set(apr_table_t *t, const char *key,
                                const char *val);

/**
* Add a key/value pair to a table, if another element already exists with the
* same key, this will over-write the old data.
* @param t The table to add the data to.
* @param key The key to use
* @param val The value to add
* @warning When adding data, this function does not make a copy of the key or
*          the value, so care should be taken to ensure that the values will
*          not change after they have been added..
*/
APR_DECLARE(void) apr_table_setn(apr_table_t *t, const char *key,
                                 const char *val);

/**
* Remove data from the table
* @param t The table to remove data from
* @param key The key of the data being removed
*/
APR_DECLARE(void) apr_table_unset(apr_table_t *t, const char *key);

/**
* Add data to a table by merging the value with data that has already been
* stored
* @param t The table to search for the data
* @param key The key to merge data for
* @param val The data to add
* @remark If the key is not found, then this function acts like apr_table_add
*/
APR_DECLARE(void) apr_table_merge(apr_table_t *t, const char *key,
                                  const char *val);

/**
* Add data to a table by merging the value with data that has already been
* stored
* @param t The table to search for the data
* @param key The key to merge data for
* @param val The data to add
* @remark If the key is not found, then this function acts like apr_table_addn
*/
APR_DECLARE(void) apr_table_mergen(apr_table_t *t, const char *key,
                                   const char *val);

/**
* Add data to a table, regardless of whether there is another element with the
* same key.
* @param t The table to add to
* @param key The key to use
* @param val The value to add.
* @remark When adding data, this function makes a copy of both the key and the
*         value.
*/
APR_DECLARE(void) apr_table_add(apr_table_t *t, const char *key,
                                const char *val);

/**
* Add data to a table, regardless of whether there is another element with the
* same key.
* @param t The table to add to
* @param key The key to use
* @param val The value to add.
* @remark When adding data, this function does not make a copy of the key or the
*         value, so care should be taken to ensure that the values will not
*         change after they have been added..
*/
APR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key,
                                 const char *val);

/**
* Merge two tables into one new table
* @param p The pool to use for the new table
* @param overlay The first table to put in the new table
* @param base The table to add at the end of the new table
* @return A new table containing all of the data from the two passed in
*/
APR_DECLARE(apr_table_t *) apr_table_overlay(apr_pool_t *p,
                                             const apr_table_t *overlay,
                                             const apr_table_t *base);

/**
* Declaration prototype for the iterator callback function of apr_table_do()
* and apr_table_vdo().
* @param rec The data passed as the first argument to apr_table_[v]do()
* @param key The key from this iteration of the table
* @param value The value from this iteration of the table
* @remark Iteration continues while this callback function returns non-zero.
* To export the callback function for apr_table_[v]do() it must be declared
* in the _NONSTD convention.
*/
typedef int (apr_table_do_callback_fn_t)(void *rec, const char *key,
                                                    const char *value);

/**
* Iterate over a table running the provided function once for every
* element in the table.  If there is data passed in as a vararg, then the
* function is only run on those elements whose key matches something in
* the vararg.  If the vararg is NULL, then every element is run through the
* function.  Iteration continues while the function returns non-zero.
* @param comp The function to run
* @param rec The data to pass as the first argument to the function
* @param t The table to iterate over
* @param ... The vararg.  If this is NULL, then all elements in the table are
*            run through the function, otherwise only those whose key matches
*            are run.
* @return FALSE if one of the comp() iterations returned zero; TRUE if all
*            iterations returned non-zero
* @see apr_table_do_callback_fn_t
*/
APR_DECLARE_NONSTD(int) apr_table_do(apr_table_do_callback_fn_t *comp,
                                     void *rec, const apr_table_t *t, ...);

/**
* Iterate over a table running the provided function once for every
* element in the table.  If there is data passed in as a vararg, then the
* function is only run on those element's whose key matches something in
* the vararg.  If the vararg is NULL, then every element is run through the
* function.  Iteration continues while the function returns non-zero.
* @param comp The function to run
* @param rec The data to pass as the first argument to the function
* @param t The table to iterate over
* @param vp The vararg table.  If this is NULL, then all elements in the
*                table are run through the function, otherwise only those
*                whose key matches are run.
* @return FALSE if one of the comp() iterations returned zero; TRUE if all
*            iterations returned non-zero
* @see apr_table_do_callback_fn_t
*/
APR_DECLARE(int) apr_table_vdo(apr_table_do_callback_fn_t *comp,
                               void *rec, const apr_table_t *t, va_list vp);

/** flag for overlap to use apr_table_setn */
#define APR_OVERLAP_TABLES_SET   (0)
/** flag for overlap to use apr_table_mergen */
#define APR_OVERLAP_TABLES_MERGE (1)
/**
* For each element in table b, either use setn or mergen to add the data
* to table a.  Which method is used is determined by the flags passed in.
* @param a The table to add the data to.
* @param b The table to iterate over, adding its data to table a
* @param flags How to add the table to table a.  One of:
*          APR_OVERLAP_TABLES_SET        Use apr_table_setn
*          APR_OVERLAP_TABLES_MERGE      Use apr_table_mergen
* @remark  This function is highly optimized, and uses less memory and CPU cycles
*          than a function that just loops through table b calling other functions.
*/
/**
*

* Conceptually, apr_table_overlap does this:
*
*  apr_array_header_t *barr = apr_table_elts(b);
*  apr_table_entry_t *belt = (apr_table_entry_t *)barr->elts;
*  int i;
*
*  for (i = 0; i < barr->nelts; ++i) {
*      if (flags & APR_OVERLAP_TABLES_MERGE) {
*          apr_table_mergen(a, belt.key, belt.val);
*      }
*      else {
*          apr_table_setn(a, belt.key, belt.val);
*      }
*  }
*
*  Except that it is more efficient (less space and cpu-time) especially
*  when b has many elements.
*
*  Notice the assumptions on the keys and values in b ― they must be
*  in an ancestor of a’s pool.  In practice b and a are usually from
*  the same pool.
*
*/

APR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b,
                                     unsigned flags);

/**
* Eliminate redundant entries in a table by either overwriting
* or merging duplicates
*
* @param t Table.
* @param flags APR_OVERLAP_TABLES_MERGE to merge, or
*              APR_OVERLAP_TABLES_SET to overwrite
*/
APR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned flags);

   
    其他apr提供的数据结构还有:hash和list,这里不再详述。
   
    (3) 字符串操作
    写过C的人都知道,处理字符串是非常头痛的问题,搞不好就内存溢出,apr也提供一些字符串函数,都是基于apr_pool_t,
使用时不用担心内存溢出的问题。
     把apr_strings.h贴出来大家一起看看,注释也比较详细,不多说:
/**
* Do a natural order comparison of two strings.
* @param a The first string to compare
* @param b The second string to compare
* @return Either <0, 0, or >0.  If the first string is less than the second
*          this returns <0, if they are equivalent it returns 0, and if the
*          first string is greater than second string it retuns >0.
*/
APR_DECLARE(int) apr_strnatcmp(char const *a, char const *b);

/**
* Do a natural order comparison of two strings ignoring the case of the
* strings.
* @param a The first string to compare
* @param b The second string to compare
* @return Either <0, 0, or >0.  If the first string is less than the second
*         this returns <0, if they are equivalent it returns 0, and if the
*         first string is greater than second string it retuns >0.
*/
APR_DECLARE(int) apr_strnatcasecmp(char const *a, char const *b);

/**
* duplicate a string into memory allocated out of a pool
* @param p The pool to allocate out of
* @param s The string to duplicate
* @return The new string
*/
APR_DECLARE(char *) apr_pstrdup(apr_pool_t *p, const char *s);

/**
* Create a null-terminated string by making a copy of a sequence
* of characters and appending a null byte
* @param p The pool to allocate out of
* @param s The block of characters to duplicate
* @param n The number of characters to duplicate
* @return The new string
* @remark This is a faster alternative to apr_pstrndup, for use
*         when you know that the string being duplicated really
*         has ‘n’ or more characters.  If the string might contain
*         fewer characters, use apr_pstrndup.
*/
APR_DECLARE(char *) apr_pstrmemdup(apr_pool_t *p, const char *s, apr_size_t n);

/**
* Duplicate at most n characters of a string into memory allocated
* out of a pool; the new string will be NUL-terminated
* @param p The pool to allocate out of
* @param s The string to duplicate
* @param n The maximum number of characters to duplicate
* @return The new string
* @remark The amount of memory allocated from the pool is the length
*         of the returned string including the NUL terminator
*/
APR_DECLARE(char *) apr_pstrndup(apr_pool_t *p, const char *s, apr_size_t n);

/**
* Duplicate a block of memory.
*
* @param p The pool to allocate from
* @param m The memory to duplicate
* @param n The number of bytes to duplicate
* @return The new block of memory
*/
APR_DECLARE(void *) apr_pmemdup(apr_pool_t *p, const void *m, apr_size_t n);

作者: 蟋蟀   发布时间: 2007-08-01

from: http://www.loveopensource.com/?p=18

Apache模块开发/用C语言扩展apache(3:一个非常简单的apache module)
by linux_prog
    有了上面几篇文章的基础,大家自己再下点功夫,应该可以去写一些简单的模块了,
下面贴出一个很简单的apache module,大家一起分析一下。
    $ cd /usr/local/apache2.2.4
    $ vi mod_c.c
#include
#include
#include “apr.h”
#include “apr_lib.h”
#include “apr_strings.h”

#define APR_WANT_STRFUNC
#include “apr_want.h”

#include “httpd.h”
#include “http_config.h”
#include “http_core.h”
#include “http_request.h”

module AP_MODULE_DECLARE_DATA c_module;

static int c_handler(request_rec *r)
{
r->content_type=”text/plain”;
ap_rprintf(r,”handler:%s\n”,r->handler);
ap_rprintf(r,”query string:%s\n”,r->args);
ap_rprintf(r,”filename:%s\n”,r->filename);
return OK;
}
static void register_hooks(apr_pool_t *p)
{
ap_hook_handler(c_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

/* module structure */
module AP_MODULE_DECLARE_DATA c_module = {
    STANDARD20_MODULE_STUFF,
    NULL,      /* dir config creater */
    NULL,                       /* dir merger ― default is to override */
    NULL,      /* server config */
    NULL,                       /* merge server configs */
    NULL,      /* command apr_table_t */
    register_hooks    /* register hooks */
};

编译并安装这个模块(apache提供的apxs非常好):
     $ ./bin/apxs -c ./mod_c.c
     $ ./bin/apxs -a -i -n c mod_c.la
     这时apxs会自动帮我们把编译好的mod_c.so安装到modules/目录中,而且httpd.conf中已经把这个module load进去了:
     [root@cn-weblog apache2.2.4]# grep mod_c conf/httpd.conf     
      LoadModule c_module           modules/mod_c.so
   
     测试这个模块:
     $ ./bin/apachectl stop
     $ ./bin/apachectl start

     在IE中访问http://myhostname/index.html?query=yy
     IE中会出现:
handler:text/html
query string:query=yy
filename:/usr/local/apache2.2.4/htdocs/index.html
     说明该module运行成功。
   
     把上面的module简单解释一下。
   
     所有的apache module都必须是这个结构体,里面要定义各个内容。
/* module structure */
module AP_MODULE_DECLARE_DATA c_module = {
    STANDARD20_MODULE_STUFF,
    NULL,      /* dir config creater */
    NULL,                       /* dir merger ― default is to override */
    NULL,      /* server config */
    NULL,                       /* merge server configs */
    //上面4项都是定义httpd.conf中命令的作用的
    NULL,      /* command apr_table_t */ //定义在httpd.conf中添加的命令,和各命令的处理函数
    register_hooks    /* register hooks */      //hooks,定义什么时候执行我们这个module的相关函数
};

    ap_hook_handler(c_handler, NULL, NULL, APR_HOOK_MIDDLE);
    表示在处理内容请求时调用我们函数�Cc_handler
   
    http://httpd.apache.org/docs/2.2/developer/
    提供了非常不错的文档,可以参考一下。

作者: 蟋蟀   发布时间: 2007-08-01

from: http://www.loveopensource.com/?p=19

Apache模块开发/用C语言扩展apache(4:一个生产环境使用的apache module�C viewvc权限控制)
by linux_prog
     下面公布一个目前在我们公司使用的apache module的源代码。
     我们公司开发人员很多,使用了SVN和viewvc来进行版本控制和查看,通过web界面,SVN能够根据每个用户的权限来控制能够
浏览某个项目下的代码,但是viewvc只要你在SVN中有用户,你就可以看当前SVN中所有项目的代码。这个风险比较大,因此,我们
开发了一个apache module,用来读取SVN的权限配置文件,把相应的权限集成到VIEWVC中。
源代码:
#include “apr_strings.h”
#include “apr_hash.h”
#include “apr_tables.h”
#include “apr_md5.h”            /* for apr_password_validate */
#include “apr_lib.h”            /* for apr_isspace */
#include “apr_base64.h”         /* for apr_base64_decode et al */
#define APR_WANT_STRFUNC        /* for strcasecmp */
#include “apr_want.h”

#include “ap_config.h”
#include “httpd.h”
#include “http_config.h”
#include “http_core.h”
#include “http_log.h”
#include “http_protocol.h”
#include “http_request.h”
#include “ap_provider.h”
#include

#define ENABLED        1
#define DISABLED       0

/* data need by our module */
typedef struct
{
    short    enabled;
    short    debug;
    char     *dir;
    // the starting path
    char     *prefixPath;
    // the stop url pattern
    char     *stopPattern;
    // the svn access file
    char     *accessFile;
} authSVN_rec;

struct access_rec
{
   // 0: group
   // 1: user
   // 2: all
   short              type;
   // the group or user name
   char              *name;
   // 0: don’t have read access
   // 1: have read access
   short             access;
   // the next access record
   struct access_rec *next;
};
module AP_MODULE_DECLARE_DATA authSVN_module;

// src starts with start
static short start_with(const char *src, const char *start)
{
int i = 0;
if(strlen(src) < strlen(start))
     return 0;

i = strlen(start) - 1;

while(i >= 0)
{
  if(src != start)
      return 0;
  i�C;
}

return 1;
}

// parse the SVN access file
static short parse_access_file(request_rec *r, const char* file,
                                     const authSVN_rec *conf,
                                     apr_hash_t* ugMap,
                                     apr_hash_t* accessMap)
{
    ap_configfile_t *f = NULL;
    apr_status_t status;
    char l[MAX_STRING_LEN], dir[256];
    status = ap_pcfg_openfile(&f, r->pool, file);
    short flag = 0;
   
    if (status != APR_SUCCESS)
        return 0;
   
    while(!(ap_cfg_getline(l, MAX_STRING_LEN, f)))
    {
        const char *w = NULL;
        char *last = NULL;
        apr_table_t *apt  = NULL;
        struct access_rec  *arec = NULL, *arecp = NULL;
      
        if ((l[0] == ‘#’) || (!l[0])) {
            continue;
        }
      
        if(start_with(l, “[groups]”)) {
            flag = 1;
            continue;
        }
      
        if(l[0] == ‘[’) {
            flag = 2;
            w = apr_strtok(l, “[]:\n”, &last);
           
            if(w && w[0] == ‘/’) {
                // the root directory
                snprintf(dir, sizeof(dir), “%s”, conf->prefixPath);
                dir[strlen(dir) - 1] = ‘\0′;
            }
            else if(w && w[0] != ‘/’)
            {
                const char *project = w;
                w = apr_strtok(NULL, “[]:\n”, &last);
                if(w)
                {
                    snprintf(dir, sizeof(dir), “%s%s%s”, conf->prefixPath, project, w);
                    // make sure the dir is not end with /
                    int len = strlen(dir);
                    if(dir[len - 1] == ‘/’) dir[len - 1] = ‘\0′;
                }
                else
                    dir[0] = ‘\0′;
            }
            else
            {
                dir[0] = ‘\0′;
            }
           
            continue;
        }
      
        if(flag == 1) {
            // this is the groups and users definition
            w = apr_strtok(l, “=, \n”, &last);
            if(w == NULL)
                // group name not found
                continue;
               
            apt = (apr_table_t *)apr_hash_get(ugMap, (const void *)w, APR_HASH_KEY_STRING);
            if(apt == NULL) {
                apt = apr_table_make(r->pool, 10);
                apr_hash_set(ugMap, (const void *)apr_pstrdup(r->pool, w),
                             APR_HASH_KEY_STRING, (const void *)apt);
            }
           
            while((w = apr_strtok(NULL, “=, \n”, &last)) != NULL) {
                // this is group name or user name
                if(w[0] == ‘@’) {
                    w++;
                if(w) {
                    apr_table_setn(apt, apr_pstrdup(r->pool, w), “0″);
                }
                }
                else
                {
                    // this is user name
                    apr_table_setn(apt, apr_pstrdup(r->pool, w), “1″);
                }
            }
           
        }
      
        if(flag == 2) {
            if(dir[0] == ‘\0′) continue;
            w = apr_strtok(l, “= \n”, &last);
            if(w)
            {
                arec = (struct access_rec *)apr_pcalloc(r->pool, sizeof(struct access_rec));
                arec->access = 0;
               
                if(w[0] == ‘@’) {
                    w++;
                    if(w) {
                        arec->name = apr_pstrdup(r->pool, w);
                        arec->type = 0;
                    }
                    else continue;
                }
                else if(w[0] == ‘*’)
                {
                    arec->name = apr_pstrdup(r->pool, “*”);
                    // this is all
                    arec->type = 2;
                }
                else
                {
                    arec->name = apr_pstrdup(r->pool, w);
                    // this is user name
                    arec->type = 1;
                }
            }
            else continue;
           
            w = apr_strtok(NULL, “= \n”, &last);
            if(!w)
            {
                arec->access = 0;
            }
            else
            {
                arec->access = 1;
            }
           
            arecp = (struct access_rec *)apr_hash_get(accessMap, (const void *)dir, APR_HASH_KEY_STRING);
            if(arecp == NULL) {
                arec->next = NULL;
                apr_hash_set(accessMap, (const void *)apr_pstrdup(r->pool, dir),
                             APR_HASH_KEY_STRING, (const void *)arec);
            }
            else
            {
                while(arecp->next != NULL) arecp = arecp->next;
                arecp->next = arec;
            }
           
        }
      
    }
   
    ap_cfg_closefile(f);
   
    return 1;
}
/* init per dir */
static void *create_authSVN_dir_config(apr_pool_t *p, char *d)
{
    authSVN_rec *conf = (authSVN_rec *)apr_pcalloc(p, sizeof(*conf));
    if(conf == NULL) return NULL;
      
    conf->enabled     = DISABLED;
    conf->debug       = DISABLED;
    conf->dir         = d;
    conf->prefixPath  = NULL;
    conf->stopPattern = NULL;
    conf->accessFile  = NULL;

    return conf;
}

/* hex to int */
static char hex2int(char c)
{
if( c>=’0′ && c<='9' ) return (c - '0');
return (c - 'A' + 10);
}

/* url decode */
static void url_decode(char *url)
{
char *p  = url;
char *p1 = NULL;

while(*p)
{
  if(*p == '+') *p = ' ';
  
  /* %AB */
  if(*p=='%' && *(p+1) && *(p+2))
  {
   *p = hex2int(toupper(*(p+1))) * 16 + hex2int(toupper(*(p+ 2)));
   strcpy(p + 1, p + 3);
  }
  /* \xAB */
  if(*p=='\\' && *(p+1) && *(p+2) && *(p+3))
       {
              p1 = p + 1;
              if(*p1 && *p1=='x')
              {
                   *p = hex2int(toupper(*(p+2))) * 16 + hex2int(toupper(*(p+3)));
                   strcpy(p+1, p+4);
              }
        }
  p++;
}

return;
}

static void parent_path(char *url)
{
    char *p = url + strlen(url) - 1;
   
    while(p != url && *p != '/') { *p = '\0'; p--; }
    if(p != url && *p=='/') *p = '\0';
   
    return;
}

// return
// 0: the user don't belong to this group
// 1: the user belong to this group
static short find_user_in_group(const char* user, const char *group, apr_hash_t* ugMap)
{
    apr_table_t *apt = (apr_table_t *)apr_hash_get(ugMap,
                                                 (const void *)group,
                                                 APR_HASH_KEY_STRING);
    if(apt == NULL) return 0;
    apr_array_header_t *arr;
    apr_table_entry_t  *elts;
    int i;
    arr  = (apr_array_header_t *)apr_table_elts(apt);
    elts = (apr_table_entry_t *)arr->elts;
   
    for(i=0; inelts; i++)
    {
        if(elts.key == NULL || elts.val == NULL) continue;
                  
        if(elts.val[0] == ‘1′ && strcmp(elts.key, user) == 0)
        {
            return 1;
        }
      
        if(elts.val[0] == ‘0′)
        {
            if(find_user_in_group(user, elts.key, ugMap))
                return 1;
        }
    }
   
    return 0;
}

// return
//  0:don’t have access
//  1:have read access
//  2:access not found
static short find_access(const char* user, const char* url,
                         apr_hash_t* ugMap, apr_hash_t* accessMap)
{
    struct access_rec *arec= (struct access_rec *)apr_hash_get(accessMap,
                             (const void *)url, APR_HASH_KEY_STRING);
   
    short access = 2;
   
    while(arec != NULL)
    {
        if(strcmp(arec->name, “*”) == 0)
        {
            // specified access to all users and groups on this url
            access = arec->access;
        }
      
        if(arec->type == 1 && strcmp(arec->name, user) == 0)
        {
            // specified user access on this url
            access = arec->access;
        }
      
        if(arec->type == 0)
        {
            // this is group access
            if(find_user_in_group(user, arec->name, ugMap))
                access = arec->access;
        }
      
        // if this user have access, we return
        if(access == 1) return access;  
      
        arec = arec->next;
    }
   
    return access;
}
static short estimate_access( request_rec *r, const authSVN_rec* conf,
                              char* url, apr_hash_t* ugMap,
                              apr_hash_t* accessMap )
{
    const char* user = r->user;
    // unauthorized
    if(!user || !user[0]) return 0;
   
    short access = find_access(user, url, ugMap, accessMap);
    if(access < 2) return access;
   
    if(url[0] == '/' && url[1] == '\0') return 0;
   
    parent_path(url);
   
    return estimate_access(r, conf, url, ugMap, accessMap);
}

// do regexp matching
static short regexp_match(char *str, char *pattern)
{
    regex_t      reg;
   regmatch_t   pm[1];
   const size_t nmatch = 1;
   int res = 0;
short r = 0;
   char ebuf[MAX_STRING_LEN];
   
res = regcomp(&reg, pattern, REG_EXTENDED);
   
if(res != 0)
   {
     regfree(&reg);
    return 0;
   }
   
   res = regexec(&reg, str, nmatch, pm, 0);
   
   if(res == REG_NOMATCH)
       r = 0;
   else
       r = 1;
   
regfree(&reg);

return r;
}

/* all pages need to pass from this handler */
static int authSVN_handler(request_rec *r)
{
        authSVN_rec *conf = ap_get_module_config(r->per_dir_config,
                                                 &authSVN_module);
        if(!conf || !conf->enabled)
             return DECLINED;
      
        if(conf->prefixPath == NULL || !start_with(r->uri, conf->prefixPath))
            return DECLINED;
      
        if(conf->stopPattern !=NULL && regexp_match(r->uri, conf->stopPattern))
            return DECLINED;
      
        apr_hash_t* ugMap     = apr_hash_make(r->pool);
        apr_hash_t* accessMap = apr_hash_make(r->pool);
      
        if(!parse_access_file(r, conf->accessFile, conf, ugMap, accessMap))
            return 403;
      
        if(conf->debug)
        {
            // run in debug mode
            // print all users/groups and access information
            apr_hash_index_t* hi;
            char *key;
            apr_table_t *val;
            struct access_rec *arec;
            apr_array_header_t *arr;
            apr_table_entry_t  *elts;
            int i;
           
            r->content_type=”text/plain”;
            ap_rprintf(r, “Parsed Users and Groups:\n”);
           
            hi = apr_hash_first(r->pool, ugMap);
            while(hi != NULL)
            {
                apr_hash_this(hi, (void *)&key, NULL, (void *)&val);
               
                ap_rprintf(r, “%s: “, key);
                arr  = (apr_array_header_t *)apr_table_elts(val);
                elts = (apr_table_entry_t *)arr->elts;
                for(i=0; inelts; i++)
                {
                    if(elts.key == NULL || elts.val == NULL) continue;
                    if(elts.val[0] == ‘0′)
                    {
                        ap_rprintf(r, “@”);
                    }
                    ap_rprintf(r, “%s “, elts.key);
                }
               
                ap_rprintf(r, “\n”);
               
                hi = apr_hash_next(hi);
            }
           
            ap_rprintf(r, “Parsed Path Access:\n”);
            hi = apr_hash_first(r->pool, accessMap);
            while(hi != NULL)
            {
                apr_hash_this(hi, (void *)&key, NULL, (void *)&arec);
                ap_rprintf(r, “%s:\n”, key);
                while(arec != NULL)
                {
                    if(arec->type == 0)
                        ap_rprintf(r, “group:%s “, arec->name);
                    else if(arec->type == 1)
                        ap_rprintf(r, “user:%s “, arec->name);
                    else
                        ap_rprintf(r, “all “);
                  
                    ap_rprintf(r, “access:%d “, arec->access);
                    ap_rprintf(r, “\n”);
                    arec = arec->next;
                }
               
                ap_rprintf(r, “\n”);
                hi = apr_hash_next(hi);
            }
        }
      
        char *url = apr_pstrdup(r->pool, r->uri);
        // decode the url for some chinese characters
        url_decode(url);
      
        // analyze the access
        if(estimate_access(r, conf, url, ugMap, accessMap))
        {
            if(conf->debug)
            {
                ap_rprintf(r, “%s have access on:%s\n”, r->user, r->uri);
                return OK;
            }
            return DECLINED;
        }
        else
        {
            if(conf->debug)
            {
                ap_rprintf(r, “%s don’t have access on:%s\n”, r->user, r->uri);
                return OK;
            }
        }
      
        return 403;
}

/* enable this module or not */
static const char *set_authSVN_enable(cmd_parms *cmd,
                                      void *mconfig,
                                      int arg)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
    conf->enabled = arg;
    return NULL;
}

/* debug this module or not */
static const char *set_authSVN_debug( cmd_parms *cmd,
                                      void *mconfig,
                                      int arg)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
    conf->debug = arg;
    return NULL;
}

/*    setting prefix path   */
static const char *set_prefix_path(cmd_parms *cmd,
                                   void *mconfig,
                                   const char *name)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
   
    if(strlen(name) <= 0)
        return "AuthSVNPrefixPath can not be null.";
   
    if(name[0] != '/' || name[strlen(name) - 1] != '/')
        return "AuthSVNPrefixPath must start and end with '/'.";
   
    conf->prefixPath = apr_pstrdup(cmd->pool, name);
   
    return NULL;
}

/*    setting stop url pattern   */
static const char *set_stop_pattern(cmd_parms *cmd,
                                    void *mconfig,
                                   const char *name)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
   
    if(strlen(name) <= 0)
        return "AuthSVNStopPattern can not be null.";
   
    conf->stopPattern = apr_pstrdup(cmd->pool, name);
   
    return NULL;
}

/* setting SVN access file  */
static const char *set_authSVN_accessFile(cmd_parms *cmd,
                                          void *mconfig,
                                          const char *name)
{
    authSVN_rec *conf = (authSVN_rec *) mconfig;
    ap_configfile_t *f = NULL;
    apr_status_t status;
   
    if(strlen(name) <= 0)
        return "SVNAccessFile can not be null.";
   
    status = ap_pcfg_openfile(&f, cmd->pool, name);

    if (status != APR_SUCCESS) {
        return “Can not open given SVN access file.”;
    }
    ap_cfg_closefile(f);
   
    conf->accessFile = apr_pstrdup(cmd->pool, name);
   
    return NULL;
}
static const command_rec auth_cmds[] =
{
    AP_INIT_FLAG(”EnableAuthSVN”, set_authSVN_enable, NULL, OR_FILEINFO,
     “enable authSVN or not.”),
    AP_INIT_FLAG(”DebugAuthSVN”,  set_authSVN_debug, NULL, OR_FILEINFO,
     “debug authSVN or not.”),
    AP_INIT_TAKE1(”AuthSVNPrefixPath”,   set_prefix_path,   NULL, OR_FILEINFO,
     “set prefix path.”),
    AP_INIT_TAKE1(”AuthSVNStopPattern”,  set_stop_pattern,   NULL, OR_FILEINFO,
     “the url pattern we do not do the access checking.”),
    AP_INIT_TAKE1(”SVNAccessFile”, set_authSVN_accessFile, NULL, OR_FILEINFO,
     “set SVN access file.”),
    { NULL }
};

static void register_hooks(apr_pool_t *p)
{
    ap_hook_handler(authSVN_handler, NULL, NULL, APR_HOOK_FIRST);
}

module AP_MODULE_DECLARE_DATA authSVN_module =
{
    STANDARD20_MODULE_STUFF,
    create_authSVN_dir_config,     /* dir config creater */
    NULL,                          /* dir merger ― default is to override */
    NULL,                          /* server config */
    NULL,                          /* merge server config */
    auth_cmds,                     /* command apr_table_t */
    register_hooks                 /* register hooks */
};

安装方法:
install the module:
./bin/apachectl stop
./bin/apxs -c mod_authSVN.c
./bin/apxs -a -i -n authSVN mod_authSVN.la
./bin/apachectl start

add the following to httpd.conf(insite the viewvc location directory):
EnableAuthSVN            on
DebugAuthSVN             off
SVNAccessFile             /usr/local/apache2.2.4/access
AuthSVNPrefixPath     /viewvc/
AuthSVNStopPattern   ^\/viewvc\/\*docroot\*\/

作者: 蟋蟀   发布时间: 2007-08-01

httpd文件的正确位置, 也可以设置任意命令行参数,使之总是有效。

httpd被调用后第一个要做的事情是找到并读取配置文件httpd.conf。此文件的位置是在编译时设定的,但也可以象下面这样在运行时用 -f 命令行可选项来指定:

/usr/local/apache/bin/httpd -f /usr/local/apache/conf/httpd.conf

如果启动时一切都很正常,服务器将与终端分离并几乎同时出现平时的命令行提示符。这代表着服务器已经启动并开始运行。然后你就可以用你的浏览器去连接你的服务器来查看DocumentRoot目录下的测试文档及其页面链接里的其它文档的本地拷贝。


启动时发生的错误
如果Apache在启动过程中产生了致命错误,它将在退出前把描述这个错误的信息显示在终端上或者写入到ErrorLog中。一个最常产生的错误信息是“Unable to bind to Port ...”。这个信息主要由以下原因造成:

想由一个特权端口启动服务但没有以root用户登录,或
启动服务时已经有另外的Apache实例在运行或其他的web服务器已经绑定了同样的端口。
更多问题的解决办法,请参见Apache的FAQ。


随系统启动
如果你希望你的服务器在系统重启后仍保持运行状态, 你应该把httpd或者apachectl的调用加入到你的系统启动文件中 (典型文件为rc.local或rc.N目录下的某一文件)。 这将会以root权限启动Apache。当然,在此之前, 你必须保证你的服务器已经完成了安全和访问权限的设定。

apachectl脚本被设计为可以用作SysV初始化脚本, 它接受start, restart, 和stop参数, 并把它们转译为httpd的对应信号, 所以通常都可以连接apachectl到适当的初始目录, 但是需要检查你的系统对此的精确要求。



  我使用服务器多半是FreeBSD系统,所以在FreeBSD之中,要开机启动Apache,一般是在/usr/local/etc/rc.d/里面建立apache.sh脚本,内容如下:


CODE:[Copy to clipboard]#!/bin/sh
###FileName=Apache.sh
###Author=HonestQiao

PREFIX=/usr/local/apache2

case "$1" in
start)
        [ "ssl" = "ssl" -a -f "$PREFIX/etc/apache2/ssl.crt/server.crt" ] && SSL=ssl
        [ -x ${PREFIX}/sbin/apachectl ] && ${PREFIX}/sbin/apachectl start${SSL} >; /dev/null && echo -n ' apache2'
        ;;
stop)
        [ -r /var/run/httpd.pid ] && ${PREFIX}/sbin/apachectl stop >; /dev/null && echo -n ' apache2'
        ;;
*)
        echo "Usage: `basename $0` {start|stop}" >;&2
        ;;
esac

exit 0
当然,你也可以在rc.local里面设置Apache 的启动,在rc.local里面添加

作者: 蟋蟀   发布时间: 2007-08-01

数组是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中的数据拷

作者: 蟋蟀   发布时间: 2007-08-01

在 httpd.conf 中设置 "HostNameLookups off" 能避免针对每个访问者的 DNS 域名的反向查询。

对于繁忙的网站,在 httpd.conf 中设置 "MaxClients 250" 或者更高。这项设置让更多的 httpd 进程同时响应请求,并避免了处理器排队的情况发生。

缺保您的 Web 页面和 CGI 页面采用了浏览器缓冲技术。例如采用 mod_gzip 加速 Zope 和 Apache保持您的 Apache 苗条,编译那些仅仅需要的模块,在编译之前,修改 src/Configuration 文件,在那些不需要的模块之前用 # 号注释掉。

如果不需要流量日志,那么把 httpd.conf 中的 TransferLog 指向到 /dev/null/除非你确定使用 .htaccess 文件来控制一些目录的权限,否则设置 "AllowOverride None" ,这样就免去 Apache 在每个目录搜索 .htaccess 文件的劳役之苦。

不要让不需要的后台进程运行。

千万不要把页面或者日志文件写到网络磁盘上,例如 NFS。

不要让 Apache (httpd) 运行于 inetd 模式。

不要让 X Windows 运行在你的 Web 服务器上,用 Ctrl-Alt-Backspace 关闭 X 。

避免使用 SSI tag。在 CGI 脚本中:

文件 I/O:打开的文件数目越少越好。

Shell 命令:采用全路径来调用 shell 命令。

如果你的网站主要以 CGI 来驱动,那么请使用 mod_perl。

在你的 Web 页面目录中,不要让文件数超过 1000 个,文件越多花费在定位上的时间也越多。

在 Web 服务器上的图片越少越好,保证每个图片都经由图片压缩器运行。

采用另外一台服务器处理图片文件。
----------------------------------------------------------------------------------------------------------------
编译
修改src/include/httpd.h
代码:--------------------------------------------------------------------------------
#ifndef HARD_SERVER_LIMIT
#ifdef WIN32
#define HARD_SERVER_LIMIT 1024
#elif defined(NETWARE)
#define HARD_SERVER_LIMIT 2048
#else
#define HARD_SERVER_LIMIT 1024
#endif
#endif
--------------------------------------------------------------------------------
在configure前添加代码:--------------------------------------------------------------------------------
CFLAGS="-O6 -mpentiumpro -fomit-frame-pointer" CXX=gcc CXXFLAGS="-O6 -mpentiumpro -fomit-frame-pointer -felide-constructors -fno-exceptions -fno-rtti"
--------------------------------------------------------------------------------例如代码:--------------------------------------------------------------------------------
CFLAGS="-O6 -mpentiumpro -fomit-frame-pointer" CXX=gcc CXXFLAGS="-O6 -mpentiumpro -fomit-frame-pointer -felide-constructors -fno-exceptions -fno-rtti" ./configure ..............
--------------------------------------------------------------------------------
注意,这里优化的是intel pentium pro以上处理器(i686)
如果你是i386,使用-m386
如果你是i486,使用-m486
如果你是i586,使用-mpentium
如果你是i686,使用-mpentiumpro
如果你是AMD CPU,使用-mcpu=k6
自行修改选项使用gmake代替make
以上为编译的优化
设置上的优化
httpd.conf代码:--------------------------------------------------------------------------------
Timeout 120
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
#超时及保持连接设置
MinSpareServers 5
MaxSpareServers 15
StartServers 8
#进程启动控制,过多不好,而且导致启动慢
MaxClients 512
#最大连接512,根据实际调整
MaxRequestsPerChild 1024
#每个进程1024只响应1024次,每响应1024就自动终止本身,并且启用新的进程
#这样可以避免一个进程占用的资源无法回收
HostnameLookups Off
#关掉连接进来的主机的名字lookup
--------------------------------------------------------------------------------减少不必要的modules,去掉不用的loadmodule,addmodule
另外,如果你是公网的一台服务器,而且对所有人开放允许连接,那么可以注释了httpd.conf里面所有order,allow,deny语句使用mod_perl代替perl,使用php的apache module代替php-cgi
添加zend optimizer
添加zend accelerator
使用zend encoder unlimited来编译php源代码
mmap_static_module:mod_mmap_static.c
mod_mmap_static.so
mmap_static_module的作用是将部分 静态文件 存放在内核缓存
不过,要小心配置mmap,因为这个会导致系统不稳定,甚至当机。另外,似乎只有xNIX主机可以使用这个。首先必须loadmodule mod_mmap_static.so或者已经编译进httpd
确认module装载了后
httpd.conf 设置语法(建议放在所有addmodule的最后,如果你没用到addmodule,那么就放在所有loadmodule后面)MMapFile 文件1 文件2 .......由于必须是静态文件,那么适合放进mmap的文件是.htm .html .txt .js还有就是图片文件,除了这些,配置文件conf,.htaccess .htpasswd之类也可以需要注意的是,mmap提供文件的缓冲数有限,大概是1000个,并且单个文件的不要太大,所有动态的文件不要放进mmap,例如.php .cgi .pl等
==========================================================================================================
maxclient默认的最大值为256,我在solaris sparc 9的机器上,如果没有重新编译,怎么都超不过这个值,重新编译的方法:
/usr/local/src/httpd-2.0.48/server/mpm/prefork/prefork.c
#define DEFAULT_SERVER_LIMIT 256
我这个值调大,我现在调整的是2000

然后安装:
./configure --prefix=/usr/local/apache2 --enable-so
make
make install

这样最大可以达到2000

作者: 蟋蟀   发布时间: 2007-08-01

昵称: 蟋蟀  时间: 2007-8-1 10:34
附件: 您所在的用户组无法下载或查看附件

作者: 蟋蟀   发布时间: 2007-08-01

HTTP请求模型

一、连接至Web服务器
一个客户端应用(如Web浏览器)打开到Web服务器的HTTP端口的一个套接字(缺省为80)。

已yahoo为例
可以用php提供的socket去操作
$sDomain = 'www.yahoo.com.cn';
$iPort   = 80;
1、用socket函数连接 (没做测试)
$rSocket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); //tcp link
if (!$rSocket) {
    printf('错误: %s',socket_strerror(socket_last_error()));
    exit;
}
$rLink   = socket_connect($rSocket,$sDomain,$iPort);
if (!$rLink) {
    printf('错误: %s',socket_strerror(socket_last_error()));
    exit;
}
$sRequest = 'GET / HTTP1.0'."\r\n\r\n"; //\r\n <CRLF>
$bWrite = socket_write($rSocket,);
if (!$bWrite) {
    printf('不能写入,%s',socket_strerror(socket_last_error()));
    exit;
}
$sContent = socket_read($rSocket,32767);
if (!sContent) {
    printf('不能读取数据,%s',socket_strerror(socket_last_error()));
    exit;
}
echo $sContent;
socket_close($rSocket); //详细说明看下面的RFC

2、使用函数fsockopen

$domain = 'www.yahoo.com.cn';
$port   = 80;
$time_limit = 30;
ini_set('max_execution_time',(ini_get('max_execution_time')>$time_limit)?ini_get('max_execution_time'):$time_limit);
ini_set('error_reporting',E_ALL);
$fp = fsockopen($domain,$port,$errno,$error,$time_limit);
if (!$fp)
{
    printf('failed: %s ',$error);
    exit('<font color="red">end</font>');
}
$cur = 'GET / HTTP/1.0'."\r\n\r\n"; //GET POST TRACE OPTIONS HEAD
fwrite($fp,$cur);
$content = '';
while (!feof($fp))
{
    $content .= fgets($fp); // 默认1000
}
fclose($fp);
echo $content


二、发送HTTP请求
     通过连接,客户端写一个ASCII文本请求行,后跟0或多个HTTP头标,一个空行和实现请求的任意数据。

一个请求由四个部分组成:请求行、请求头标、空行和请求数据

1.请求行:请求行由三个标记组成:请求方法、请求URI和HTTP版本,它们用空格分隔。
例如:GET /index.html HTTP/1.1

HTTP          规范定义了8种可能的请求方法:
GET              检索URI中标识资源的一个简单请求
HEAD            与GET方法相同,服务器只返回状态行和头标,并不返回请求文档
POST            服务器接受被写入客户端输出流中的数据的请求
PUT                 服务器保存请求数据作为指定URI新内容的请求
DELETE            服务器删除URI中命名的资源的请求
OPTIONS          关于服务器支持的请求方法信息的请求
TRACE             Web服务器反馈Http请求和其头标的请求
CONNECT        已文档化但当前未实现的一个方法,预留做隧道处理

2.请求头标:由关键字/值对组成,每行一对,关键字和值用冒号(:)分隔。
请求头标通知服务器有关于客户端的功能和标识,典型的请求头标有:
User-Agent        客户端厂家和版本
Accept            客户端可识别的内容类型列表
Content-Length    附加到请求的数据字节数

3.空行:最后一个请求头标之后是一个空行,发送回车符和退行,通知服务器以下不再有头标。
4.请求数据:使用POST传送数据,最常使用的是Content-Type和Content-Length头标。
三、服务端接受请求并返回HTTP响应
Web服务器解析请求,定位指定资源。服务器将资源副本写至套接字,在此处由客户端读取。

一个响应由四个部分组成;状态行、响应头标、空行、响应数据

1.状态行:状态行由三个标记组成:HTTP版本、响应代码和响应描述。
HTTP版本:向客户端指明其可理解的最高版本。
响应代码:3位的数字代码,指出请求的成功或失败,如果失败则指出原因。
响应描述:为响应代码的可读性解释。
例如:HTTP/1.1 200 OK

HTTP响应码:
1xx:信息,请求收到,继续处理
2xx:成功,行为被成功地接受、理解和采纳
3xx:重定向,为了完成请求,必须进一步执行的动作
4xx:客户端错误:
2.响应头标:像请求头标一样,它们指出服务器的功能,标识出响应数据的细节。
3.空行:最后一个响应头标之后是一个空行,发送回车符和退行,表明服务器以下不再有头标。
4.响应数据:HTML文档和图像等,也就是HTML本身。

四、服务器关闭连接,浏览器解析响应
1.浏览器首先解析状态行,查看表明请求是否成功的状态代码。
2.然后解析每一个响应头标,头标告知以下为若干字节的HTML。
3.读取响应数据HTML,根据HTML的语法和语义对其进行格式化,并在浏览器窗口中显示它。
4.一个HTML文档可能包含其它需要被载入的资源引用,浏览器识别这些引用,对其它的资源再进行额外的请求,此过程循环多次。

五、无状态连接
HTTP模型是无状态的,表明在处理一个请求时,Web服务器并不记住来自同一客户端的请求。

六、实例
1.浏览器发出请求
GET /index.html HTTP/1.1

服务器返回响应:
HTTP /1.1 200 OK
Date: Apr 11 2006 15:32:08 GMT
Server: Apache/2.0.46(win32)
Content-Length: 119
Content-Type: text/html

<HTML>
<HEAD>
<LINK REL="stylesheet" HREF="index.css">
</HEAD>
<BODY>
<IMG SRC="image/logo.png">
</BODY>
</HTML>

2.浏览器发出请求
GET /index.css HTTP/1.1

服务器返回响应:
HTTP /1.1 200 OK
Date: Apr 11 2006 15:32:08 GMT
Server: Apache/2.0.46(win32)
Connection: Keep-alive, close
Content-Length: 70
Content-Type: text/plane

h3{
    font-size:20px;
    font-weight:bold;
    color:#005A9C;
}

3.浏览器发出请求
GET image/logo.png HTTP/1.1

服务器返回响应:
HTTP /1.1 200 OK
Date: Apr 11 2006 15:32:08 GMT
Server: Apache/2.0.46(win32)
Connection: Keep-alive, close
Content-Length: 1280
Content-Type: text/plane

{Binary image data follows}


(附录)
1.HTTP规范:Internet工程制定组织(IETF)发布的RFC指定Internet标准,这些RFC被Internet研究发展机构广泛接受。因为它们是标准文档,故一般用正规语言编写,如立法文标一样。

2.RFC:RFC一旦被提出,就被编号且不会再改变,当一个标准被修改时,则给出一个新的RFC。作为标准,RFC在Internet上被广泛采用。

3.HTTP的几个重要RFC:
    RFC1945    HTTP 1.0 描述
    RFC2068    HTTP 1.1 初步描述
    RFC2616    HTTP 1.1 标准
4.资源标识符URI(Uniform Resource Identifter,URI)


HTTP参考

一、HTTP码应码
响应码由三位十进制数字组成,它们出现在由HTTP服务器发送的响应的第一行。

响应码分五种类型,由它们的第一位数字表示:
1.1xx:信息,请求收到,继续处理
2.2xx:成功,行为被成功地接受、理解和采纳
3.3xx:重定向,为了完成请求,必须进一步执行的动作
4.4xx:客户端错误,请求包含语法错误或者请求无法实现
5.5xx:服务器错误,服务器不能实现一种明显无效的请求

下表显示每个响应码及其含义:
100            继续
101            分组交换协
200            OK
201            被创建
202            被采纳
203            非授权信息
204            无内容
205            重置内容
206            部分内容
300            多选项
301            永久地传送
302            找到
303            参见其他
304            未改动
305            使用代理
307            暂时重定向
400            错误请求
401            未授权
402            要求付费
403            禁止
404            未找到
405            不允许的方法
406            不被采纳
407            要求代理授权
408            请求超时
409            冲突
410            过期的
411            要求的长度
412            前提不成立
413            请求实例太大
414            请求URI太大
415            不支持的媒体类型
416            无法满足的请求范围
417            失败的预期
500            内部服务器错误
501            未被使用
502            网关错误
503            不可用的服务
504            网关超时
505            HTTP版本未被支持

二、HTTP头标
头标由主键/值对组成。它们描述客户端或者服务器的属性、被传输的资源以及应该实现连接。

四种不同类型的头标:
1.通用头标:即可用于请求,也可用于响应,是作为一个整体而不是特定资源与事务相关联。
2.请求头标:允许客户端传递关于自身的信息和希望的响应形式。
3.响应头标:服务器和于传递自身信息的响应。
4.实体头标:定义被传送资源的信息。即可用于请求,也可用于响应。

头标格式:<name>:<value><CRLF>

下表描述在HTTP/1.1中用到的头标
Accept       定义客户端可以处理的媒体类型,按优先级排序;
                 在一个以逗号为分隔的列表中,可以定义多种类型和使用通配符。例如:Accept: image/jpeg,image/png,*/*
Accept-Charset
                 定义客户端可以处理的字符集,按优先级排序;
                 在一个以逗号为分隔的列表中,可以定义多种类型和使用通配符。例如:Accept-Charset: iso-8859-1,*,utf-8

Accept-Encoding        定义客户端可以理解的编码机制。例如:Accept-Encoding:gzip,compress
Accept-Language       定义客户端乐于接受的自然语言列表。例如:Accept-Language: en,de

Accept-Ranges
             一个响应头标,它允许服务器指明:将在给定的偏移和长度处,为资源组成部分的接受请求。
            该头标的值被理解为请求范围的度量单位。例如Accept-Ranges: bytes或Accept-Ranges: nonea

Age       允许服务器规定自服务器生成该响应以来所经过的时间长度,以秒为单位。
            该头标主要用于缓存响应。例如:Age: 30

Allow            一个响应头标,它定义一个由位于请求URI中的次源所支持的HTTP方法列表。例如:Allow: GET,PUT

aUTHORIZATION
            一个响应头标,用于定义访问一种资源所必需的授权(域和被编码的用户ID与口令)。
            例如:Authorization: Basic YXV0aG9yOnBoaWw=

Cache-Control        一个用于定义缓存指令的通用头标。例如:Cache-Control: max-age=30
Connection           一个用于表明是否保存socket连接为开放的通用头标。例如:Connection: close或Connection: keep-alive

Content-Base  
            一种定义基本URI的实体头标,为了在实体范围内解析相对URLs。
            如果没有定义Content-Base头标解析相对URLs,使用Content-Location URI(存在且绝对)或使用URI请求。
            例如:Content-Base: Http://www.myweb.com

Content-Encoding    一种介质类型修饰符,标明一个实体是如何编码的。例如:Content-Encoding: zip
Content-Language    用于指定在输入流中数据的自然语言类型。例如:Content-Language: en
Content-Length        指定包含于请求或响应中数据的字节长度。例如:Content-Length:382

Content-Location
            指定包含于请求或响应中的资源定位(URI)。
            如果是一绝。对URL它也作为被解析实体的相对URL的出发点。
            例如:Content-Location: http://www.myweb.com/news

Content-MD5
            实体的一种MD5摘要,用作校验和。
            发送方和接受方都计算MD5摘要,接受方将其计算的值与此头标中传递的值进行比较。
            例如:Content-MD5: <base64 of 128 MD5 digest>

Content-Range
            随部分实体一同发送;标明被插入字节的低位与高位字节偏移,也标明此实体的总长度。
            例如:Content-Range: 1001-2000/5000

Contern-Type        标明发送或者接收的实体的MIME类型。例如:Content-Type: text/html
Date                    发送HTTP消息的日期。例如:Date: Mon,10PR 18:42:51 GMT

ETag      一种实体头标,它向被发送的资源分派一个唯一的标识符。
            对于可以使用多种URL请求的资源,ETag可以用于确定实际被发送的资源是否为同一资源。
            例如:ETag: "208f-419e-30f8dc99"

Expires          指定实体的有效期。例如:Expires: Mon,05 Dec 2008 12:00:00 GMT
Form            一种请求头标,给定控制用户代理的人工用户的电子邮件地址。例如:From: [email protected]
Host            被请求资源的主机名。对于使用HTTP/1.1的请求而言,此域是强制性的。例如:Host: www.myweb.com

If-Modified-Since   
            如果包含了GET请求,导致该请求条件性地依赖于资源上次修改日期。
            如果出现了此头标,并且自指定日期以来,此资源已被修改,应该反回一个304响应代码。
            例如:If-Modified-Since: Mon,10PR 18:42:51 GMT

If-Match            如果包含于一个请求,指定一个或者多个实体标记。只发送其ETag与列表中标记区配的资源。
                       例如:If-Match: "208f-419e-308dc99"

If-None-Match
           如果包含一个请求,指定一个或者多个实体标记。资源的ETag不与列表中的任何一个条件匹配,操作才执行。
           例如:If-None-Match: "208f-419e-308dc99"

If-Range
            指定资源的一个实体标记,客户端已经拥有此资源的一个拷贝。必须与Range头标一同使用。
            如果此实体自上次被客户端检索以来,还不曾修改过,那么服务器只发送指定的范围,否则它将发送整个资源。
            例如:Range: byte=0-499<CRLF>If-Range:"208f-419e-30f8dc99"

If-Unmodified-Since
            只有自指定的日期以来,被请求的实体还不曾被修改过,才会返回此实体。
            例如:If-Unmodified-Since:Mon,10PR 18:42:51 GMT

Last-Modified        指定被请求资源上次被修改的日期和时间。例如:Last-Modified: Mon,10PR 18:42:51 GMT
Location
            对于一个已经移动的资源,用于重定向请求者至另一个位置。
            与状态编码302(暂时移动)或者301(永久性移动)配合使用。
            例如:Location: http://www2.myweb.com/index.jsp

Max-Forwards
            一个用于TRACE方法的请求头标,以指定代理或网关的最大数目,该请求通过网关才得以路由。
            在通过请求传递之前,代理或网关应该减少此数目。例如:Max-Forwards: 3

Pragma            一个通用头标,它发送实现相关的信息。例如:Pragma: no-cache
Proxy-Authenticate
           类似于WWW-Authenticate,便是有意请求只来自请求链(代理)的下一个服务器的认证。
           例如:Proxy-Authenticate: Basic realm-admin

Proxy-Proxy-Authorization
            类似于授权,但并非有意传递任何比在即时服务器链中更进一步的内容。
            例如:Proxy-Proxy-Authorization: Basic YXV0aG9yOnBoaWw=

Public            列表显示服务器所支持的方法集。例如:Public: OPTIONS,MGET,MHEAD,GET,HEAD
Range            指定一种度量单位和一个部分被请求资源的偏移范围。例如:Range: bytes=206-5513

Refener
            一种请求头标域,标明产生请求的初始资源。对于HTML表单,它包含此表单的Web页面的地址。
            例如:Refener: http://www.myweb.com/news/search.html

Retry-After
           一种响应头标域,由服务器与状态编码503(无法提供服务)配合发送,以标明再次请求之前应该等待多长时间。
           此时间即可以是一种日期,也可以是一种秒单位。例如:Retry-After: 18

Server                     一种标明Web服务器软件及其版本号的头标。例如:Server: Apache/2.0.46(Win32)
Transfer-Encoding     一种通用头标,标明对应被接受方反向的消息体实施变换的类型。例如:Transfer-Encoding: chunked

Upgrade
            允许服务器指定一种新的协议或者新的协议版本,与响应编码101(切换协议)配合使用。
            例如:Upgrade: HTTP/2.0

User-Agent
            定义用于产生请求的软件类型(典型的如Web浏览器)。
            例如:User-Agent: Mozilla/4.0(compatible; MSIE 5.5; Windows NT; DigExt)

Vary          一个响应头标,用于表示使用服务器驱动的协商从可用的响应表示中选择响应实体。例如:Vary: *
Via            一个包含所有中间主机和协议的通用头标,用于满足请求。例如:Via: 1.0 fred.com, 1.1 wilma.com
Warning    用于提供关于响应状态补充信息的响应头标。例如:Warning: 99 www.myweb.com Piano needs tuning

www-Authenticate
            一个提示用户代理提供用户名和口令的响应头标,与状态编码401(未授权)配合使用。响应一个授权头标。
            例如:www-Authenticate: Basic realm=zxm.mgmt
本文主要讲解了HTTP.其实我的个人目的是说明socket.只要网络操作都是用socket实现的。
如whois查询(43) ftp上传下载(21) ssh(22)连接(未操作过应该可以的) smtp(25)发送邮件(要求认证操作的用ehlo不用helo)
pop3(110)
下面是收邮件的例子
$popServer = 'pop.mail.yahoo.com.cn';
$iPort     = 110;
$rHandle   = fsockopen($popServer,$iPort);
if (!$rHandle) {
    echo '不能连接';
    exit;
}
$sWriteUser = 'USER '.你的用户名."\r\n";
$bWriteUser = fwrite($rHandle,$sWriteUser);
$sReadUser  = fgets($rHandle,1000);
if (!eregi("OK",$sReadUser)) {
    echo '没有返回信息';
    exit;
}
$sWritePass = 'Pass '.你的密码."\r\n";
$bWritePass = fwrite($rHandle,$sWritePass);
$sReadPass  = fgets($rHandle,1000);
if (!eregi("OK",sReadPass)) {
    echo '没有返回信息';
    exit;
}
$sWriteList = 'list'."\r\n";
$bWriteList = fwrite($rHandle,$bWriteList);
$sReadList  = fgets($rHandle,32767);
echo $sReadList; //用户信件列表
fclose($rHandle); 程序没有调试,只是说明一个意思,具体命令可以查看RFC

其实上面的例子完全可以用telnet 去、操作
telnet www.yahoo.com.cn 80
GET / HTTP/1.0 <CRLF>

作者: 蟋蟀   发布时间: 2007-08-01

文件是由很多独立的tag组成的。每一个tag都包括一个头和一个数据体.
就像web中服务器信息的头标组成了文件头。这些tag像mime的格式

作者: 蟋蟀   发布时间: 2007-08-01

不错,顶.............................

作者: 蟋蟀   发布时间: 2007-08-01

调优 Apache

Apache 是一种高度可配置的软件。它具有大量特性,但每一种都代价高昂。从某种程度上来说,调优 Apache 来说就是以恰当的方式分配资源,还涉及到将配置简化为仅包含必要内容。

配置 MPM

Apache 是模块化的,因为可以轻松添加和移除特性。在 Apache 的核心,多处理模块(Multi-Processing Module,MPM)提供了这种模块化功能性 ―― 管理网络连接、调度请求。MPM 使您能够使用线程,甚至能够将 Apache 迁移到另外一个操作系统。

每次只能有一个 MPM 是活动的,必须使用 --with-mpm=(worker|prefork|event) 静态编译。

每个请求使用一个进程的传统模型称为 prefork。较新的线程化模型称为 worker,它使用多个进程,每个进程又有多个线程,这样就能以较低的开销获得更好的性能。最新的 event MPM 是一种实验性的模型,为不同的任务使用单独的线程池。要确定当前使用的是哪种 MPM,可执行 httpd -l。

选择使用何种 MPM 取决于许多因素。在 event MPM 脱离实验状态之前,不应考虑这种模型,而是在使用线程和不使用线程之间作出选择。表面上看来,如果所有底层模块(包括 PHP 使用的所有库)都是线程安全的,线程要优于分叉(forking)。而 Prefork 是较为安全的选择;如果选择了 worker,则应该谨慎测试。性能收益还取决于您的发布版所附带的库及硬件。

无论选择了哪种 MPM,都必须恰当地配置它。一般而言,配置 MPM 包括告知 Apache 怎样去控制有多少 worker 正在运行,它们是线程还是进程。prefork MPM 的重要配置选项如清单 1 所示。


清单 1. prefork MPM 的配置
               
StartServers       50
MinSpareServers   15
MaxSpareServers   30
MaxClients       225
MaxRequestsPerChild  4000


编译您自己的软件

最初使用 UNIX® 时,我坚持为加入系统的一切编译软件。最终,维护更新给我带来了麻烦,所以我学会了如何构建包来简化这一任务。后来我意识到,大多数时候我都在重复做发布版做过的事情。现在,在很大程度上来说,我会尽可能坚持使用我所选择的发布版提供的一切,仅在必要的时候使用自己的包。

类似地,您可能会发现,就可维护性而言,使用厂商提供的软件包要优于使用最新、最棒的代码。有些时候,性能调优和系统管理的目标会有所冲突。如果使用商业版的 Linux 或依赖于第三方支持,那么可能不得不考虑厂商的支持。

如果您一意孤行,那么请学会如何构建能与您的发布版协同工作的包,请学会如何将其集成到补丁系统之中。这将确保软件,以及您作出的任何更改得到一致的构建,且能跨多个系统使用。还应订阅恰当的邮件列表和 RSS 提要来及时获得软件更新。


prefork 模型会为每个请求创建一个新进程。多余的进程保持空闲,以处理传入的请求,这缩短了启动延迟。只要 Web 服务器出现,预先完成的配置就会立即启动 50 个进程,并尽力保持 10 到 20 个空闲服务器运行。进程数的硬性限制由 MaxClients 指定。尽管一个进程能够处理许多相继的请求,Apache 还是会取消连接数超过 4,000 以后的进程,这降低了内存泄漏的风险。

配置线程化 MPM 与之类似,不同之处只是必须确定使用多少线程和进程。Apache 文档解释了所有必要的参数和计算。

要经过几次尝试和出错之后才能选好要使用的值。最重要的值是 MaxClients。目标在于允许足够多的 workder 进程或线程运行,同时又不会导致服务器进行过度的交换。如果传入的请求超出处理能力,那么至少满足此值的那些请求会得到服务,其他请求被阻塞。

如果 MaxClients 过高,那么所有客户机都将体验到糟糕的服务,因为 Web 服务器会试图换出一个进程,以使另一个进程能够运行。而设得过低意味着可能会不必要地拒绝服务。查看高负载下运行的进程数量和所有 Apache 进程所导致的内存占用情况对设置这个值很有帮助。如果 MaxClients 的值超过 256,必须将 ServerLimit 也设为同样的数值,请仔细阅读 MPM 的文档,了解相关信息。

根据服务器的角色调优要启动和保持空闲的服务器数量。如果服务器仅运行 Apache,那么可以使用适中的值,如 清单 1 所示,因为这样就能充分利用机器。如果系统中还有其他数据库或服务器,那么就应该限制运行中的空闲服务器的数量。

有效地使用选项和重写

Apache 处理的每个请求都要履行一套复杂的规则,这些规则指明了 Web 服务器必须遵循的约束或特殊指令。对文件夹的访问可能按 IP 地址约束为某个特定文件夹,也可配置用户名和密码。这些选项还包含处理特定文件,例如,如果提供了一个目录列表,该如何处理的文件,或输出结果是否应压缩。

这些配置以 httpd.conf 中容器的形式出现,例如 <Directory>,以便指定所用配置引用的是磁盘上的一个位置;再如 <Location>,表示引用是 URL 中的路径。清单 2 展示了一个实际的 Directory 容器。


清单 2. 为根目录应用的一个 Directory 容器
               
<Directory />
    AllowOverride None
    Options FollowSymLinks
</Directory>



在清单 2 中,位于一对 Directory 和 /Directory 标记之间的配置应用于给定目录和该目录下的一切内容 ―― 在本例中,这个给定目录是根目录。此处,AllowOverride 标记指出,用户不允许重写任何选项(稍后将进一步介绍)。FollowSymLinks 选项被启用,它允许 Apache 查看之前的符号连接来为请求提供服务,即便文件位于包含 Web 文件的目录之外。这就意味着,如果 Web 目录中的一个文件是 /etc/passwd 的符号连接,Web 服务器将在请求时顺利为该文件提供服务。如果使用了 -FollowSymLinks,该特性就会被禁用,同样的请求将致使为客户机返回错误。

最后这个场景正是导致两方面关注的原因所在。第一个方面与性能有关。如果禁用了 FollowSymLinks,Apache 就必须检查使用该文件名的所有组件(目录和文件本身),以确保它们不是符号连接。这会带来额外的开销(磁盘操作)。另外一个称为 FollowSymLinksIfOwnerMatch 的选项会在文件所有者与连接所有者相同时使用符号连接。为获得最佳性能,请使用 清单 2 中的选项。

至此,有安全意识的读者应该有了警惕的感觉。安全性永远是功能性与风险之间的权衡。在我们的例子中,功能性是速度,而风险是允许对系统上的文件进行未经授权的访问。缓解风险的措施之一是 LAMP 应用服务器通常专注于一种具体功能,用户无法创建危险的符号连接。如果有必要启用符号连接,那么可以将其约束在文件系统的特定区域,如清单 3 所示。


清单 3. 将 FollowSymLinks 约束为一个用户的目录
               
<Directory />
   Options FollowSymLinks
</Directory>

<Directory /home/*/public_html>
   Options -FollowSymLinks
</Directory>



在清单 3 中,一个用户的主目录中的任何 public_html 目录及其所有子目录都移除了 FollowSymLinks 选项。

如您所见,通过主服务器配置,可为每个目录单独配置选项。用户可以自行重写这种服务器配置(如果管理员通过 AllowOverrides 语句允许了这种操作),只需将一个 .htaccess 文件放入目录即可。该文件包含额外的服务器指令,每次请求包含 .htaccess 文件的目录时将加载并应用这些指令。尽管之前探讨过系统没有用户的问题,但许多 LAMP 应用程序都利用这种功能性来控制访问、实现 URL 重写,因此有必要理解其工作原理。

即便 AllowOverrides 语句能阻止用户去做您不希望他们做的事,Apache 也必须检查 .htaccess 文件,看看是否有要完成的工作。父目录可以指定由来自子目录的请求处理的指令,这也就表示,Apache 必须搜索所请求文件的目录树的所有组件。可想而知,这会使每次请求都导致大量磁盘操作。

最简单的解决方案是不允许重写,这能消除 Apache 检查 .htaccess 的需求。之后的任何特殊配置都将直接放在 httpd.conf 中。清单 4 显示为对一个用户的项目目录进行密码检查向 httpd.conf 增加的代码,而不是将其放入一个 .htaccess 文件并依赖于 AllowOverrides。


清单 4. 将 .htaccess 配置移入 httpd.conf
               
<Directory /home/user/public_html/project/>
  AuthUserFile /home/user/.htpasswd
  AuthName "uber secret project"
  AuthType basic
  Require valid-user
</Directory>



如果配置转移到 httpd.conf 中,且 AllowOverrides 被禁用,磁盘的使用就能减少。一个用户的项目可能不会吸引许多人来点击,但设想一下,将这项技术应用于一个忙碌的站点时会有多么强大。

有时不可能彻底消除 .htaccess 文件的使用。例如,在清单 5 中,一个选项被约束到文件系统的特定部分,重写也可以是有作用域的。


清单 5. 限定 .htaccess 检查的作用域
               
<Directory />
  AllowOverrides None
</Directory>

<Directory /home/*/public_html>
  AllowOverrides AuthConfig
</Directory>



实现清单 5 之后,Apache 会在父目录中查找 .htaccess 文件,但会在 public_html 目录处停止,因为文件系统的其余部分禁用了此功能。例如,如果请求的是一个映射到 /home/user/public_html/project/notes.html 的文件,那么仅有 public_html 和 project 目录被搜索。

关于每目录单独配置的最后一个提示就是:要按顺序依次进行。任何介绍 Apache 调优的的文章都会告诉您,应通过 HostnameLookups off 指令禁用 DNS 查找,因为试图反向解析连接到您的服务器的所有 IP 地址无疑是浪费资源。然而,基于主机名的任何约束都会迫使 Web 服务器对客户机的 IP 地址执行反向查找,对其结果进行正向查找,以验证该名称的真实性。因此,避免使用基于客户主机名的访问控制,在必须使用时限定其作用域,这些都是明智的做法。

持久连接

一个客户机连接到 Web 服务器时,允许客户机通过同一个 TCP 连接发出多个请求,这减少了与多个连接相关的延迟。在一个 Web 页面引用了多幅图片时,这就很有用:客户机可以通过一个连接先请求页面,再请求所有图片。其缺点在于服务器上的 worker 进程必须等待客户机要关闭的会话,之后才能转到下一个请求。

Apache 使您能够配置如何处理持久连接(称为 keepalives)。httpd.conf 全局级的 KeepAlive 5 允许服务器在连接强制关闭之前处理一个连接上的 5 个请求。将此值设置为 0 将禁用持久连接。同样位于全局级上的 KeepAliveTimeout 确定在会话关闭之前,Apache 将等待另外一个连接多久。

持久连接的处理并非 “一刀切” 式的配置。对于某些 Web 站点,禁用 keepalives 更合适(KeepAlive 0);而对于其他一些站点,启用它会带来巨大的收益。惟一的解决之道就是尝试使用这两种配置,自己观察哪种更合适。但若启用了 keepalives,使用较小的超时时间较为明智,例如 2,即 KeepAliveTimeout 2。这能确保希望发出另外一个请求的客户机有充足的时间,还能确保 worker 进程不会一直空闲,等待可能永远不会出现的下一个请求。

压缩

Web 服务器能够在将输出发回给客户机之前压缩它。这将使通过 Internet 发送的页面更小,代价是 Web 服务器上的 CPU 周期。对于那些负担得起 CPU 开销的服务器来说,这是提高页面下载速度的好办法 ―― 页面压缩后大小变为原来的三分之一这种事情并不罕见。

图片通常已经是压缩过的,因此压缩应仅限于文本输出。Apache 通过 mod_deflate 提供压缩。尽管 mod_deflate 可轻松启用,但它涉及到太多的复杂性,很多手册都解释了这些复杂的内容。本文不会介绍压缩的配置,但提供了相应文档的链接(参见 参考资料 部分

作者: 蟋蟀   发布时间: 2007-08-01

昵称: lmhllr  时间: 2007-8-1 10:46
用apache2。0时,经常会遇到无法重启的问题,重启时,apache的errorlog中会报
[Wed Dec 21 13:59:51 2005] [crit] (17)File exists: unable to create scoreboard "/var/program/apache/logs/eyou.scoreboard" (name
-based shared memory failure)
以前将ScoreBoardFile /var/program/apache/logs/control.scoreboard改成其他名字就会解决问题,后来发现这个并不是解决办法的根本方法,用过几次后就会失效,gogle一下发现了真正原因,并且也学会了ipcs+ipcrm的方法

Failure restarting Apache HTTP Server
I finally couldn't take it any longer. It was a minor snap, but snap I did. I was doing a quick configuration change on one of my many web sites hosted on www.hacksaw.ca. After I did a quick addition of a new virtual host I issued the /etc/init.d/httpd restart command to get the http daemon to restart with the new configuration. Of course this failed with the following pesky error message:

[Tue Dec 14 10:31:50 2004] [crit] (17)File exists: unable to create scoreboard (name-based shared memory failure)

This has happened many times before. I have always been forced to do a reboot of my server to clear the shared memory issue. However, today I was stuck on a long conference call and decided to figure out how to fix this issues without a full system restart. I could not find a site that described how to fix this problem, but after looking at the code and a bit of googling I did find a simple solution.

What is the problem?
The problem is quite simple. Apache had an ungraceful shutdown (for some other reason) and did not free up a shared memory segment. When httpd is restarted it discovers that its shared memory segement already exists, thinks an instance is already running, and teminates because it cannot allocate the resources it needs.

How do I fix the problem?
To fix the problem without requiring a system restart we need to find the orphan shared memory segment. To do this we need to use the ipcs command as root to list allocated segments. On my normal system the shared memory usage would look like the following:
[root@hacksaw /]# ipcs -m -p

------ Shared Memory Creator/Last-op --------
shmid owner cpid lpid
0 root 1006 12254
327681 hacksaw 8640 8641
262146 hacksaw 1405 1205
294915 hacksaw 1408 1414
360452 chainsaw 8724 8732

[root@hacksaw hacksaw]# ipcs -m

------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x0101f3fe 0 root 600 56344 14
0x00000000 327681 hacksaw 600 393216 2 dest
0x00000000 262146 hacksaw 600 393216 2 dest
0x00000000 294915 hacksaw 600 393216 2 dest
0x00000000 360452 chainsaw 600 393216 2 dest

You will notice 14 attached proceses (nattach) to the first shared memory segment (segment 0). These are the 14 httpd threads waiting for incoming http requests. Now here is what it looks like after the failure to restart:

[root@hacksaw /]# ipcs -m -p

------ Shared Memory Creator/Last-op --------
shmid owner cpid lpid
0 root 1006 1109
327681 hacksaw 8640 8641
262146 hacksaw 1405 1205
294915 hacksaw 1408 1414
360452 chainsaw 8724 8732

[root@hacksaw /]# ipcs -m

------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x0101f3fe 0 root 600 56344 0
0x00000000 327681 hacksaw 600 393216 2 dest
0x00000000 262146 hacksaw 600 393216 2 dest
0x00000000 294915 hacksaw 600 393216 2 dest
0x00000000 360452 chainsaw 600 393216 2 dest


Notice that there are no attached clients to the first segment (identified by shmid 0). Apache had a failure on shutdown and did not clean up after itself. Just to make sure we can do a ps command and look to see if the process id associated with the memory segment exists.
[root@hacksaw /]# ps 1006
PID TTY STAT TIME COMMAND
[root@hacksaw /]#

Now we remove the shared memory associated with ID 0 using the ipcrm command.
[root@hacksaw /]# ipcrm -m 0

As we can see in the below output the memory segment has been removed. We can now restart apache successfully.
[root@hacksaw /]# ipcs -m

------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 327681 hacksaw 600 393216 2 dest
0x00000000 262146 hacksaw 600 393216 2 dest
0x00000000 294915 hacksaw 600 393216 2 dest
0x00000000 360452 chainsaw 600 393216 2 dest


我的处理:
[root@localhost logs]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2129920    nobody    600        640        16
0x00000000 2162689    nobody    600        640        16
0x01027191 1769474    root      600        209176     0
0x01027192 1966083    root      600        209176     0
0x01027193 2097156    root      600        209176     0

[root@localhost logs]# ipcrm shm 1769474
resource(s) deleted
[root@localhost logs]# ipcrm shm 1966083
resource(s) deleted
[root@localhost logs]# ipcrm shm 2097156
resource(s) deleted
[root@localhost logs]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x00000000 2129920    nobody    600        640        16
0x00000000 2162689    nobody    600        640        16

作者: 蟋蟀   发布时间: 2007-08-01

非常好,就是太多了

作者: ssdduy   发布时间: 2007-08-01

这个好贴子怎么到最后面了哦?

作者: 蟋蟀   发布时间: 2007-08-01

顶一下!

作者: lmhllr   发布时间: 2007-08-01

建议置顶!!!

作者: 蟋蟀   发布时间: 2007-08-01

建议置顶!!!

作者: unspace   发布时间: 2007-08-01

好东东  顶下

作者: chaizhiyong   发布时间: 2007-11-15

作者: 楚风   发布时间: 2008-01-30

热门下载

更多