Title: Thread-Sicherheit unter Apache 2.0




Dokumentation für Entwickler




Wird eines der Threading verwendenden MPMs unter Apache 2.0 benutzt, dann ist es wichtig, dass jede vom Apache aufgerufene Funktion Thread-sicher ist. Beim Einbinden von Erweiterungen anderer Hersteller kann es schwierig werden, festzustellen, ob der Server am Ende Thread-sicher ist. Ein oberflächlicher Test gibt darüber keinen Aufschluss, da Probleme der Thread-Sicherheit zu subtilen Konkurrenzverhältnissen führen können, die nur bei sehr starker Serverauslastung erkennbar sind.

Globale und statische Variablen

Wenn Sie Ihre Module programmieren und dabei feststellen möchten, ob ein Modul oder eine Bibliothek eines anderen Herstellers Thread-sicher ist, müssen Sie einige allgemeine Dinge beachten.

Sie müssen sich zuerst klar machen, dass in einem Thread-Modell jeder einzelne Thread seinen eigenen Programmzähler, Stack sowie eigene Register hat. Lokale Variablen befinden sich im Stack und breiten keine Probleme. Aufpassen müssen Sie bei statischen und globalen Variablen. Prinzipiell ist es nicht unzulässig, statische oder globale Variablen zu benutzen. Manchmal sind tatsächlich alle Threads betroffen, aber im Allgemeinen müssen sie vermieden werden, wenn der Code Thread-sicher sein soll.

In einer Situation, in der eine Variable global und für alle Threads zugänglich sein muss, sollten Sie vorsichtig sein, wenn Sie sie aktualisieren. Handelt es sich beispielsweise um einen Zähler, der erhöht wird, dann müssen Sie ihn abgekoppelt erhöhen, um konkurrierende Zugriffe durch andere Threads zu vermeiden. Benutzen Sie hierfür einen Mutex (gegenseitiger Ausschluss). Blockieren Sie den Mutex, lesen Sie den aktuellen Wert, erhöhen Sie ihn, schreiben Sie ihn zurück und heben Sie anschließend die Blockade des Mutex auf. Jeder andere Thread, der den Wert verändern möchte, muss zuerst den Mutex überprüfen und ist bis zu dessen Freigabe blockiert.

Wenn Sie das APR verwenden, dann achten Sie auf die Funktionen apr_atomic_* und apr_thread_mutex_*.

errno

Diese allgemein gebräuchliche globale Variable speichert die Fehlernummer des zuletzt aufgetretenen Fehlers. Ruft ein Thread eine maschinenorientierte Funktion auf, die errno setzt und die Variable wird anschließend von einem anderen Thread überprüft, dann gehen Fehlernummern von einem Thread an den anderen über. Um dieses Problem zu lösen, müssen Sie sicherstellen, dass Modul oder Bibliothek _REENTRANT definieren oder mit -D_REENTRANT kompiliert werden. Dadurch wird errno zu eine Thread-bezogenen Variablen und sollte für den Code transparent sein. Das kann ungefähr wie folgt geschehen:

#define errno (*(__errno_location()))

Beim Zugriff auf errno wird die Funktion __errno_location() aus der Bibliothek libc aufgerufen. Wird _REENTRANT gesetzt, müssen auch einige andere Funktionen in ihre *_r-Äquivalente umdefiniert werden; manchmal sind auch Änderungen der allgemeinen Makros gusw/putc in sicherere Funktionsaufrufe erforderlich. Überprüfen Sie Dokumentation Ihre libc-Bibliothek bezüglich der Einzelheiten. Anstatt oder zusätzlich zu _REENTRANT bewirken dies auch die Symbole _POSIX_C_SOURCE, _THREAD_SAFE, _SVID_SOURCE und _BSD_SOURCE.

Problematische Standardfunktionen

Funktionen müssen nicht nur Thread-sicher sondern auch ablaufvariant sein. Die Funktion strtok() ist ein Beispiel dafür. Sie können sie zum ersten Mal mit dem Begrenzer aufrufen, den sich die Funktion merkt und die bei jedem späteren Aufruf das nächste Element zurückgibt. Wird sie von mehreren Threads aufgerufen, dann führt das eindeutig zu Problemen. Die meisten Systeme verfügen über eine ablaufvariante Version der Funktion mit dem Namen strtok_r(), der ein zusätzliches allokiertes char *-Argument übergeben wird, welches die Funktion an Stelle des eigenen statischen Speichers für die Statusüberwachung verwendet. Wenn Sie das APR verwenden, können Sie apr_strtok() benutzen.

crypt() ist eine weitere Funktion, die nicht unbedingt ablaufvariant ist, so dass bei Aufrufen dieser Funktion aus einer Bibliothek Vorsicht geboten ist. Bei einigen Systemen ist sie ablaufvariant, so dass sie nicht immer ein Problem darstellt. Verfügt Ihr System über crypt_r(), dann sollten Sie diese Funktion benutzen oder gegebenenfalls den Aufruf mit md5 ganz umgehen.

Verbreitete Bibliotheken anderer Hersteller

Die folgende Liste führt allgemein verbreitete Bibliotheken anderer Hersteller von Apache-Modulen auf. Anhand dieser Liste können Sie überprüfen, ob Ihr Modul eine möglicherweise unsichere Bibliothek mit Funktionen wie ldd(1) und nm(1) benutzt. Für PHP können Sie beispielsweise folgendes ausprobieren:

% 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)

Neben diesen Bibliotheken müssen Sie auch alle statisch in das Modul eingebundenen Bibliotheken beachten. Mit nm(1) können Sie nach einzelnen Symbolen im Modul suchen.

Liste der Bibliotheken

Bitte senden Sie eine Nachricht an [email protected], falls Ergänzungen oder Korrekturen an dieser Liste erforderlich sind.

BibliothekVersionThread-sicher?Anmerkungen
ASpell/PSpell ?
Berkeley DB 3.x, 4.x Ja Seien Sie vorsichtig bei der gemeinsamen Nutzung einer Verbindung durch Threads.
bzip2 Ja APIs auf höherer und unterer Ebene sind Thread-sicher. Auf höherer Ebene wird aber Thread-sicherer Zugriff auf errno benötigt.
cdb ?
C-Client Vielleicht C-Client benutzt die Funktionen strtok() und gethostbyname(), die bei den meisten Implementierungen von C-Bibliotheken nicht Thread-sicher sind. Die statischen Daten von C-Client sollen von den Threads gemeinsam benutzt werden. Wenn strtok() und gethostbyname() unter Ihrem Betriebssystem Thread-sicher sind, ist C-Client vielleicht Thread-sicher.
cpdflib ?
libcrypt ?
Expat Ja Benötigt eine eigene Parser-Instanz pro Thread.
FreeTDS ?
FreeType ?
GD 1.8.x ?
GD 2.0.x ?
gdbm Nein Fehlerrückgabe über eine statische gdbm_error-Variable
ImageMagick 5.2.2 Ja Die ImageMagick-Dokumentationen behaupten, es sei seit Version 5.2.2 Thread-sicher (siehe Change log).
Imlib2 ?
libjpeg v6b ?
libmysqlclient Ja Benutzen Sie die Variante mysqlclient_r der Bibliothek, um Thread-Sicherheit zu gewährleisten. Weiter Informationen finden Sie unter http://www.mysql.com/doc/en/Threaded_clients.html.
Ming 0.2a ?
Net-SNMP 5.0.x ?
OpenLDAP 2.1.x Ja Benutzen Sie die Variante ldap_r der Bibliothek, um Thread-Sicherheit zu gewährleisten.
OpenSSL 0.9.6g Ja Erfordert eine saubere Verwendung von CRYPTO_num_locks, CRYPTO_set_locking_callback und CRYPTO_set_id_callback
liboci8 (Oracle 8+) 8.x,9.x ?
pdflib 5.0.x Ja Die PDFLib-Dokumentationen behaupten, die Bibliothek sei Thread-sicher. In der Datei changes.txt steht, sie sei seit Version 1.91 partiell Thread-sicher: http://www.pdflib.com/products/pdflib/index.html.
libpng 1.0.x ?
libpng 1.2.x ?
libpq (PostgreSQL) 7.x Ja Benutzen Sie keine Verbindungen gemeinsam mit mehreren Threads und achten Sie auf crypt()-Aufrufe.
Sablotron 0.95 ?
zlib 1.1.4 Ja Basiert auf den Thread-sicheren Funktionen zalloc und zfree. Standardmäßig werden die Funktionen calloc und free benutzt, die Thread-sicher sind.
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to