Apache APR可移植运行库简介

来源:http://blog.csdn.net/tingya

 

 

1.1 何为APR?
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库版本进行检查。

 

 

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的目录组织
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选择你需要编译的实际项目:
 

编译后aprXXX项目生成的静态库通常位于对应目录下的LibD目录中,而libXXX项目生成的动态库则位于Debug或者Release目录下,具体取决于当前是发行版本还是调试版本。

 

 

1.4 应用APR
我们首先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 –ladm
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的原因。关于内存池的详细细节,我们在第二章详细讨论。

 

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。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值