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。