Apache APR可移植运行库简介(4)

转载请注明来源:http://blog.csdn.net/tingya
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不是我们的重点,此处不
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值