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。
关于作者
张中庆,目前主要的研究方向是嵌入式浏览器,移动中间件以及大规模服务器设计。目前正在进行Apache的源代码分析,计划出版《Apache源代码全景分析》上下册。Apache系列文章为本书的草案部分,对Apache感爱好的朋友可以通过flydish1234 at sina.com.cn与之联系!
假如你觉得本文不错,请点击文后的“推荐本文”链接!!