Apache HTTP Server 2.4 版
其他語言: en
在 Apache HTTP Server 2.x 中使用任何執行緒的 mpm 時,很重要的是 Apache 呼叫的每個函數都必須執行緒安全。在連結第 3 方延伸時,很難確定產生的伺服器是否會執行緒安全。隨意測試通常無法得知,因為執行緒安全問題可能導致精細的競爭狀況,而此狀況可能只會在負載加重時出現在特定條件下。
在撰寫您的模組或嘗試確定模組或第 3 方程式庫是否執行緒安全時,有一些常見事項必須牢記。
首先,您需要了解在執行緒模型中,每個個別執行緒都有自己的程式計數器、堆疊和暫存器。區域變數存在於堆疊上,所以這些變數沒問題。您需要留心靜態變數或全域變數。這並不表示您絕對不能使用靜態變數或全域變數。有些情況下,您實際上希望某些內容影響所有執行緒,但一般來說,如果您希望您的程式碼執行緒安全,您需要避免使用這些變數。
如果您有一個需要為全域且所有執行緒都必須存取的全域變數,在更新該變數時要十分小心。例如,如果該變數是一個增量計數器,您需要進行原子增量,以避免與其他執行緒發生競爭狀況。您可以使用互斥鎖 (mutual exclusion) 執行此動作。鎖定互斥鎖、讀取目前值、增量該值並將其寫回,然後再解鎖互斥鎖。任何其他希望修改該值的執行緒都必須先檢查互斥鎖,並在清除該值之前進行封鎖。
如果您使用 APR,請查看 apr_atomic_*
函數和 apr_thread_mutex_*
函數。
這是共同使用的全局變數,它保存上一次錯誤發生的錯誤編號。如果一個執行緒呼叫設定 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
。
除了執行緒安全之外,函式也必須具備再入性。strtok()
就是一個明顯的範例。您第一次呼叫它並提供分隔符號,隨後函式將記住此分隔符號,而且在每次後續呼叫時皆會傳回下一個代號。顯然地,如果有多個執行緒呼叫此函式,您將會遇到問題。大多數系統都有函式 strtok_r()
的再入性版本,您可以在其中傳入一個額外參數,該參數包含配置的 char *
,函式將使用此 char *
取代其自有的靜態儲存空間來維持代號化狀態。如果您使用 APR,您可以使用 apr_strtok()
。
crypt()
是另一個傾向於非再入性的函式,因此如果您在函式庫中發現有呼叫此函式的情況,請特別注意。在某些系統上,此函式具備再入性,因此這不總是會造成問題。如果您的系統有 crypt_r()
,您應該使用此系統,或者如果可能,使用 md5 來完全避免這個問題。
以下是第三方 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)
來尋找模組中的個別符號。
如果您對此清單有補充或修正,請寄信至 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_locks 、CRYPTO_set_locking_callback 、CRYPTO_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