2013-04-11 6 views
10

sich das folgende triviale C-Programm,Was ist der richtige Weg, um eine thread-sichere, plattformübergreifende C-Bibliothek zu erstellen?

#include <errno.h> 
int 
main(int argc, char* argv[]) { 
    return errno; 
} 

Wenn auf Solaris kompiliert, das Verhalten dieses Codes ist abhängig von der Anwesenheit von -D_REENTRANT.

solaris$ cc -E test.c | grep return 
    return errno; 
solaris$ cc -D_REENTRANT -E test.c | grep return 
    return (* (___errno ())); 

mit der letzteren Version threadsicher sein. Wenn wir den gleichen Code auf Linux kompilieren, bekommen wir das gleiche Verhalten unabhängig von -D_REENTRANT

linux$ gcc -E test.c | grep return 
    return (*__errno_location()); 
linux$ gcc -D_REENTRANT -E test.c | grep return 
    return (*__errno_location()); 

Solaris' cc hat die Option -mt, die -D_REENTRANT impliziert, ebenso wie gcc ‚s -pthread. Bei einer Bibliothek scheint die Angabe dieser Multi-Thread-Optionen jedoch schlecht, da sie eine unnötige Abhängigkeit von der Laufzeit des Threads darstellt. Wenn die Bibliothek jedoch threadsicher (einschließlich Fehlernummern) sein muss, wird die threadsichere Semantik sowohl zur Kompilierungszeit der Bibliothek als auch zum Ableiten des Codes benötigt. Unter Linux ist das einfach, weil errno immer Thread-lokal ist, aber das ist auf anderen Systemen nicht garantiert, wie gerade gezeigt wurde.

Das führt zu der Frage: Wie wird eine threadsichere Bibliothek ordnungsgemäß kompiliert und mit Kopfzeilen verteilt? Eine Option wäre #define _REENTRANT im Hauptheader, aber dies würde Probleme verursachen, wenn #include <errno.h> vor der Bibliothek Header-Aufnahme auftritt. Eine andere Option besteht darin, die Bibliothek mit -D_REENTRANT zu kompilieren und den Hauptkopf #error zu haben, wenn _REENTRANT nicht definiert ist.

Was ist der richtige/beste Weg, um eine thread-sichere Bibliothek zu erstellen und sicherzustellen, dass sie korrekt mit dem Code, mit dem sie verknüpft ist, zusammenarbeitet?

Antwort

4

Ich habe momentan keinen Zugriff auf Solaris-Maschinen, daher kann ich das nicht testen. Aber was passiert, wenn Sie #define _POSIX_C_SOURCE 200112L als die erste Zeile in test.c setzen (vor der Aufnahme von <errno.h>)? Wenn Ihr Solaris POSIX-kompatibel ist, sollte dies errno auf die Thread-sichere Version erweitern. Dies liegt daran, POSIX errno definiert wie folgt:

Für jeden Thread eines Prozesses, der Wert von errno nicht berührt, indem Funktionsaufrufe oder Zuweisungen errno durch andere Threads.

Dementsprechend ist dies für jedes POSIX-kompatible System tragbar. Wenn Sie POSIX-kompatiblen Anwendungscode schreiben möchten, sollten Sie immer define _POSIX_C_SOURCE auf den Wert für die POSIX-Mindestversion, die Sie anvisieren, definieren. Die Definition sollte am Anfang jeder Quelldatei stehen, bevor Header hinzugefügt werden. Von der Version 2001 des Standards:

Eine strikt konforme POSIX-Anwendung ist eine Anwendung, die nur die in IEEE Std 1003.1-2001 beschriebenen Funktionen erfordert. Eine solche Anwendung:

...

8.Für die C-Programmiersprache soll _POSIX_C_SOURCE definieren 200112L zu sein, bevor eine Kopf

+0

'cc -D_POSIX_C_SOURCE = 200112L -E test.c | grep return -> return (* (___errno())); ' Tatsächlich funktioniert das wie erwartet; Empfiehlst du auch, dies in der Kopfzeile der POSIX-kompatiblen Bibliothek zu definieren? –

+0

@AlexanderChernyakhovsky: Nein, es muss nie in einer Header-Datei definiert werden. Es sollte immer am Anfang der Quelldateien definiert werden. Nur so kann sichergestellt werden, dass es vor dem Einfügen von Kopfzeilen definiert ist. Sie können es auch wie in Ihrem Beispiel in der Befehlszeile definieren. Das ist akzeptabel. Ich bevorzuge es in der Quelldatei zu sehen, aber das ist teilweise nur eine Frage des Geschmacks. –

+0

Interessant. Es klingt, als würdest du dafür plädieren, immer noch nach einem "# define" in meinem Bibliothekskopf zu suchen, weil ich immer noch nicht möchte, dass ein Endentwickler das falsche errno bekommt. –

0

enthalten ist Wenn Ihre Bibliothek verwendet autoconf, haben Sie wahrscheinlich die AC_USE_SYSTEM_EXTENSIONS Makro verwenden möchten. Dieses Makro setzt einige zielspezifische Definitionen, die die POSIX + -Erweiterungssemantik ermöglichen. Ich habe momentan kein Solaris-System zum Testen, aber ich glaube, _POSIX_PTHREAD_SEMANTICS sollte das thread-sichere errno aktivieren. Zumindest aktiviert es die POSIX _r() - Funktionen und nicht die POSIX-Entwürfe _r() - Varianten, die Solaris standardmäßig störend zur Verfügung stellt.