<-
Apache > HTTP Server > 文件 > 2.4 版本 > 開發人員文件

Apache HTTP Server 2.x 執行緒安全問題

其他語言:  en 

在 Apache HTTP Server 2.x 中使用任何執行緒的 mpm 時,很重要的是 Apache 呼叫的每個函數都必須執行緒安全。在連結第 3 方延伸時,很難確定產生的伺服器是否會執行緒安全。隨意測試通常無法得知,因為執行緒安全問題可能導致精細的競爭狀況,而此狀況可能只會在負載加重時出現在特定條件下。

Support Apache!

另請參閱

top

全域變數和靜態變數

在撰寫您的模組或嘗試確定模組或第 3 方程式庫是否執行緒安全時,有一些常見事項必須牢記。

首先,您需要了解在執行緒模型中,每個個別執行緒都有自己的程式計數器、堆疊和暫存器。區域變數存在於堆疊上,所以這些變數沒問題。您需要留心靜態變數或全域變數。這並不表示您絕對不能使用靜態變數或全域變數。有些情況下,您實際上希望某些內容影響所有執行緒,但一般來說,如果您希望您的程式碼執行緒安全,您需要避免使用這些變數。

如果您有一個需要為全域且所有執行緒都必須存取的全域變數,在更新該變數時要十分小心。例如,如果該變數是一個增量計數器,您需要進行原子增量,以避免與其他執行緒發生競爭狀況。您可以使用互斥鎖 (mutual exclusion) 執行此動作。鎖定互斥鎖、讀取目前值、增量該值並將其寫回,然後再解鎖互斥鎖。任何其他希望修改該值的執行緒都必須先檢查互斥鎖,並在清除該值之前進行封鎖。

如果您使用 APR,請查看 apr_atomic_* 函數和 apr_thread_mutex_* 函數。

top

errno

這是共同使用的全局變數,它保存上一次錯誤發生的錯誤編號。如果一個執行緒呼叫設定 errno 的低層級函式,然後另一個執行緒檢查該函式,我們會將錯誤編號從一個執行緒導至另一個執行緒。為了解決此問題,請確定模組或函式庫定義了 _REENTRANT 或透過 -D_REENTRANT 編譯。這將使 errno 成為每執行緒變數,並且希望能讓程式碼便於使用。它是透過類似下列方法執行的

#define errno (*(__errno_location()))

意即存取 errno 將呼叫由 libc 提供的 __errno_location()。設定 _REENTRANT 也強制將其他一些函式重新定義為其 *_r 等效項,而且有時會將常見的 getc/putc 巨集變更為更安全的函式呼叫。請檢查您的 libc 文件以取得詳細資訊。除了 _REENTRANT 之外,可能會影響此結果的符號還有 _POSIX_C_SOURCE_THREAD_SAFE_SVID_SOURCE_BSD_SOURCE

top

常見的標準問題函式

除了執行緒安全之外,函式也必須具備再入性。strtok() 就是一個明顯的範例。您第一次呼叫它並提供分隔符號,隨後函式將記住此分隔符號,而且在每次後續呼叫時皆會傳回下一個代號。顯然地,如果有多個執行緒呼叫此函式,您將會遇到問題。大多數系統都有函式 strtok_r() 的再入性版本,您可以在其中傳入一個額外參數,該參數包含配置的 char *,函式將使用此 char * 取代其自有的靜態儲存空間來維持代號化狀態。如果您使用 APR,您可以使用 apr_strtok()

crypt() 是另一個傾向於非再入性的函式,因此如果您在函式庫中發現有呼叫此函式的情況,請特別注意。在某些系統上,此函式具備再入性,因此這不總是會造成問題。如果您的系統有 crypt_r(),您應該使用此系統,或者如果可能,使用 md5 來完全避免這個問題。

top

常見的第三方函式庫

以下是第三方 Apache 模組使用的常見函式庫清單。您可以透過使用 ldd(1)nm(1) 等工具來檢查您的模組是否使用到潛在不安全的函式庫。例如,對於 PHP,請嘗試下列方式

% ldd libphp4.so
libsablot.so.0 => /usr/local/lib/libsablot.so.0 (0x401f6000)
libexpat.so.0 => /usr/lib/libexpat.so.0 (0x402da000)
libsnmp.so.0 => /usr/lib/libsnmp.so.0 (0x402f9000)
libpdf.so.1 => /usr/local/lib/libpdf.so.1 (0x40353000)
libz.so.1 => /usr/lib/libz.so.1 (0x403e2000)
libpng.so.2 => /usr/lib/libpng.so.2 (0x403f0000)
libmysqlclient.so.11 => /usr/lib/libmysqlclient.so.11 (0x40411000)
libming.so => /usr/lib/libming.so (0x40449000)
libm.so.6 => /lib/libm.so.6 (0x40487000)
libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x404a8000)
libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0x404e7000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0x40505000)
libssl.so.2 => /lib/libssl.so.2 (0x40532000)
libcrypto.so.2 => /lib/libcrypto.so.2 (0x40560000)
libresolv.so.2 => /lib/libresolv.so.2 (0x40624000)
libdl.so.2 => /lib/libdl.so.2 (0x40634000)
libnsl.so.1 => /lib/libnsl.so.1 (0x40637000)
libc.so.6 => /lib/libc.so.6 (0x4064b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)

除了這些函式庫之外,您還需要查看靜態連結到模組中的任何函式庫。您可以使用 nm(1) 來尋找模組中的個別符號。

top

函式庫清單

如果您對此清單有補充或修正,請寄信至 dev@httpd.apache.org

函式庫版本執行緒安全性註解
ASpell/PSpell ?
Berkeley DB 3.x, 4.x 小心不要在執行緒間共用連線。
bzip2 低層級和高層級 API 都是執行緒安全的。不過,高層級 API 需要對 errno 執行緒安全存取。
cdb ?
C-Client 或許 c-client 使用的 strtok()gethostbyname() 在大多數 C 函式庫實作中並非執行緒安全。c-client 的靜態資料用意是在執行緒間共用。如果 strtok()gethostbyname() 在您的作業系統中是執行緒安全的,則 c-client 可能會 是執行緒安全的。
libcrypt ?
Expat 每個執行緒需要一個獨立的剖析器執行個體
FreeTDS ?
FreeType ?
GD 1.8.x ?
GD 2.0.x ?
gdbm 透過靜態 gdbm_error 變數傳回錯誤
ImageMagick 5.2.2 ImageMagick 的文件宣稱從 5.2.2 版開始執行緒安全(請參閱 變更日誌)。
Imlib2 ?
libjpeg v6b ?
libmysqlclient 使用 mysqlclient_r 函式庫變體來確保執行緒安全性。如需更多資訊,請閱讀 https://mysqldev.dev.org.tw/doc/mysql/en/Threaded_clients.html
Ming 0.2a ?
Net-SNMP 5.0.x ?
OpenLDAP 2.1.x 使用 ldap_r 函式庫變體來確保執行緒安全性。
OpenSSL 0.9.6g 需要適當使用 CRYPTO_num_locksCRYPTO_set_locking_callbackCRYPTO_set_id_callback
liboci8 (Oracle 8+) 8.x,9.x ?
pdflib 5.0.x PDFLib 的文件宣稱執行緒安全;changes.txt 指出從 V1.91 開始部分執行緒安全:http://www.pdflib.com/products/pdflib-family/pdflib/
libpng 1.0.x ?
libpng 1.2.x ?
libpq (PostgreSQL) 8.x 不要在執行緒間共用連線,並注意 crypt() 呼叫
Sablotron 0.95 ?
zlib 1.1.4 依賴於執行緒安全的 zalloc 和 zfree 函式。預設使用 libc 的 calloc/free,它們是執行緒安全的。

其他語言:  en 

top

留言

注意事項
這不是一問一答區。這區留言應著重於改善文件或伺服器建議,如果已實作或被視為無效/離題,我們的版主可能會移除這些留言。關於如何管理 Apache HTTP 伺服器的問題,應導向我們的 IRC 頻道 (#httpd) 上 Libera.chat,或寄送至我們的 郵寄清單