Wo gibt es zumindest für (ANSI-)C eine Liste der thread-unsaven Funktionen? Von strtok ist ja bekannt dass es thread-unsave ist, aber welche anderen Funktionen sind es noch?
Was bedeutet "thread-unsaven" ? Hat das nur was mit C++ zu tun ? In normalen ANSI-C Büchern finde ich es jedenfalls nicht. Peter
Hallo Peter, das ist der neudeutsche Ausdruck für etwas, was uC-Programmierer ohnehin verinnerlicht haben (sollten): Wenn eine Routine eine andere Routine unterbricht und nach getaner Arbeit wieder zur Ursprungsroutine zurückkehrt, dann gibt es zwei Möglichkeiten: Es wurden alle Register und ggf. Variablen zuvor gesichert und am Ende wiederhergestellt, dann ist das "thread-safe", oder man hat das nicht getan, dann ist die Routine "thread-unsave". Das ist jedenfalls ein simples Beispiel. Das Thema kann man wahlweise auch beliebig komplex gestalten... ;-) Gruß, Frank
Wozu muß man das wissen ? Das ist doch nicht mein Problem, da hat sich gefälligst der Compiler drum zu kümmern. Wenn ich eine Variable später verwende, muß entweder der Aufrufer sie sichern oder der Aufgerufene. Je nach Maschine kann das eine oder das andere günstiger sein. Das ist also keine C-Frage, sondern eine Frage der Implementierung des Compilers. Selbst in verschiedenen Versionen eines Compilers kann das völlig unterschiedlich gelöst sein. Wissen müßte man sowas also nur, wenn man Assembler includiert, aber sowas mache ich nicht. Bzw. wenn doch, dann lasse ich mir vom Compiler einen Rumpf basteln (nach Assembler compilieren) und füge dort meinen Assemblercode ein. Peter
>> da hat sich gefälligst der Compiler
drum zu kümmern.
Yepp, sollte er, macht er vermutlich auch. AFAIK ist das auch kein
Problem des Compilers, sondern der Bibliotheken. Ich kenne das Thema
auch eher aus dem Bereich der PC-Programmierung, denn da gibt es jede
Menge Bibliotheken, die eben nicht thread-safe sind. Von C habe ich
nicht genug Ahnung, um auf die Ursprungsfrage antworten zu können.
Gruß, Frank
Also tread-unsave bedeutet, dass die funktion nicht von mehreren Threads (ohne getrennten Speicherbereich) benutzt werden kann; das hat mit Registern usw. nix zu tun. strtok ist thread-unsave, weil es nur nur einen buffer verwendet und alle Threads darauf zugreifen. Bei den meisten anderen Funktionen wie z. B. strlen gibt es das Problem nicht. Und bisher habe ich noch keinen Compiler gesehen, der bei tread-unsaven Funktionen warnt, selbst wenn der Code mit solchen Funktionen immer einen Seiteneffekt produziert. Im Embedded-Bereich bekommt man vom Compiler meist nicht einmal eine Warnung bei Anweisungen ohne Effekt (z. B. {a==b;}) oder auch nur undefinierten Ausdrücken (z. B. {a[i] = i++;} oder memmove(b, ++b, 9)). Deshalb suche ich noch eine Liste der tread-unsaven Funktionen.
Das kann von Compiler zu Compiler unterschiedlich sein; bei ICC-430 sind alle Funktionen bis auf strtok reentrant, bei anderen Compilern weiß ich es nicht. Eine Frage beim Hersteller/in der entsprechenden Mailingliste sollte Klarheit schaffen. Andreas
Aha. Also ist es wohl so, dass nach dem ANSI-Standard nur strtok nicht thread-save ist, oder?
Der C-Standard sagt über Threads überhaupt nichts aus, ob die Standard-Bibliothek thread-safe ist oder nicht ist also vollkommen abhängig von der Implementation.
Also bei strtok steht es im Standard und auch in Büchern wie "Programmieren in C"; ansonsten wäre der Standard ja überflüssig! Was dort nicht festgelegt ist, ist welche Operationen atomar sind, aber das hängt ja auch von der Hardware ab.
>> das hat mit Registern usw. nix zu tun. Und ob! Das Register-Beispiel ist nur die unterste Ebene des Problems. Nach dem Compilieren geht es auf Maschinenebene sehr wohl um Register. Denn wenn die betreffende Funktion sich schon nicht um die Register kümmert, dann helfen besondere Maßnahmen auf höheren Ebenen (Buffer sichern, etc.) auch nichts mehr. Wenn es um eine möglichst schnelle Ausführung einer Funktion geht, kann man z.B. das Statusregister temporär in ein anderes freies Register laden und später daraus zurückkopieren. Das bringt beim AVR z.B. eine Einsparung von 2 Taktzyklen gegenüber der Sicherung auf dem Stack. Das funktioniert beim "normalen" Aufruf dieser Funktion auch prima. Wird diese Funktion jetzt aber während ihrer Ausführung von einem Interrupt unterbrochen und die Interruptroutine ruft die Funktion erneut auf, dann knallt es. Diese Lösung ist also nicht thread-safe, während die Stack-Lösung durchaus wiedereintrittsfähig sein kann, wenn innerhalb der Routine nicht noch anderer Murks passiert. Gruß, Frank
> Wenn es um eine möglichst schnelle Ausführung einer Funktion geht, > kann man z.B. das Statusregister temporär in ein anderes freies > Register laden und später daraus zurückkopieren. ... Wird diese > Funktion jetzt aber während ihrer Ausführung von einem > Interrupt unterbrochen und die Interruptroutine ruft die Funktion > erneut auf, dann knallt es. Ja, aber das kann nur in Assembler passieren, denn nach dem Standard sind die meisten Funktionen thread-save und können problemlos mehrfach aufgerufen werden. Da darf es höchstens Seiteneffekte bei nicht atomaren Operationen geben. Also Funktionen mit Umlagern von Registern sind garantiert nicht ANSI-C-kompatibel!
Mag sein. Da Peter aber auch viel Assembler macht, fand ich die Erläuterung auf Assemblerebene angebracht, da sie das Prinzip verdeutlicht. Aus C-Diskussionen halte ich mich mangels tiefergehender Kenntnisse lieber raus, aber wenn Du Dir so sicher bist, daß die notwendigen Angaben im Standard festgelegt sind, warum schaust Du denn nicht dort nach? Gruß, Frank
@Rolf F.: An welcher Stelle im Standard steht das? Ich lese auf Seite 184 von http://wwwold.dkuug.dk/jtc1/sc22/open/n2794/n2794.txt: [#4] The functions in the standard library are not guaranteed to be reentrant and may modify objects with static storage duration.146) Und die dazugehörige Fußnote: 146Thus, a signal handler cannot, in general, call standard library functions.
Wenn ich das richtig verstanden habe, dann ist mit "thread-save" einfach nur reentrant gemeint. Da wir hier ja über Mikrokontroller reden, dürfte das nur in wenigen Fällen überhaupt eine Rolle spielen. Beim Keil C51 sind generell alle Funktionen nicht reentrant. Das ist auch sinnvoll, damit man auch die kleinen mit nur 128 Byte RAM effektiv in C programmieren kann. Und sobald man dieselbe Funktion im main() und in einem Interrupt aufruft, gibts ein Warning. Dann muß man diese Funktion als reentrant definieren, was dann aber mehr Speicherverbrauch und längere Laufzeit bewirkt. Oftmals fährt man dann günstiger, wenn man diese Funktion nochmal mit einem anderen Namen definiert (zumindest laufzeitmäßig). Ob Bibliotheksfunktionen auch als reentrante Version vorliegen, habe ich noch nicht probiert. Generell sollen ja Interrupts sehr schnell sein, d.h. Unterprogrammaufrufe und erst recht langdauernde Stringoperationen sollte man da nur sehr sparsam einsetzen. Aber ich stimme auch den meisten zu, daß solche Feinheiten nur an der jeweiligen Version eines Compilers und seiner Bibliotheken festzumachen sind und nicht generell für C gelten. Peter
Aha, dann sind also die Funktionen von stdlib.h generell nicht thread-save und die anderen aber schon (sofern nichts anderes daneben steht wie bei strtok). Ich hatte das nicht gefunden, weil ich den Standard nicht komplett gelesen und nach thread und nicht reentrant gesucht hatte. Dann muss ich mal in der vorhandenen Software nachsehen, ob in den ISRs etwas aus stdlib.h ist und das thread-save ersetzen.
"standard library" bedeutet nicht stdlib.h, sondern alle Standard-Funktionen aus string.h, strdlib.h, math.h, usw. Obwohl der C-Standard nicht garantiert dass die Funktionen reentrant sind, können sie es je nach Implementation natürlich sein.
Achso. In dem Standard konnte ich keine Definition von "standard library" finden (der Begriff wird zwar verwendet, aber nicht definiert) und bin deshalb davon ausgegangen, dass damit stdlib.h gemeint war. Wenn das nicht so ist, ist damit wohl der Umfang der "Standard headers", also <assert.h> <inttypes.h> <signal.h> <stdlib.h> <complex.h> <iso646.h> <stdarg.h> <string.h> <ctype.h> <limits.h> <stdbool.h> <tgmath.h> <errno.h> <locale.h> <stddef.h> <time.h> <fenv.h> <math.h> <stdint.h> <wchar.h> <float.h> <setjmp.h> <stdio.h> <wctype.h> gemeint, oder? Weil im Standard nicht steht, dass die anderen Bibliotheken nicht reentrant sein könnten, folgt dass sie reentrant sein müssen, oder?
Hhm, die Erfahrung zeigt, dass man solche Texte besser anders herum interpretiert: Wenn die Wiedereintrittsfähigkeit nicht ausdrücklich zugesichert ist, dann sollte man sich auch nicht darauf verlassen. Gruß, Frank
Ich hab mir mal die BC-Hilfe zu strtok angesehen, jetzt kapier ichs endlich. "thread-unsaven" hat überhaupt nichts mit reentrant zu tun. strtok kann man nämlich auch mit einen Null-Zeiger aufrufen, um in dem selben String das Token ein weiteres mal zu suchen, d.h. die Funktion merkt sich, wo sie das letzte mal gestanden hat. Also auch, wenn eine Funktion ein Token sucht, und dann eine Unterfunktion aufruft, die zwischenzeitlich ein anderes Token sucht, dann krachts. Da aber ein Zeiger auf den gefundenen Teilstring geliefert wird, braucht man sich diesen ja nur zu merken um danach die nächste Suche zu beginnen und alles ist in Butter. "thread-unsaven" sind also alle Funktionen, die einen weiteren Durchgang machen können, aber dafür kein extra Datenfeld vom Aufrufer belegen, wo sie irgendwas ablegen können. Z.B. ist findfirst/findnext "thread-saven", da der Aufrufer eine entsprechende Datenstruktur zur Verfügung stellen muß. Sonst könnte man ja nicht rekursiv einen Verzeichnisbaum durchsuchen. Peter
> "thread-unsaven" sind also alle Funktionen, die einen weiteren > Durchgang machen können, aber dafür kein extra Datenfeld vom > Aufrufer belegen, wo sie irgendwas ablegen können. Diese sind es auf jeden Fall. Generell sind alle Funktionen nicht thread-safe, die sich in irgendeiner Form Zwischenergebnisse ,,merken'' (also die in ihrer Implementierung irgendwo Variablen benutzen, die als ,static' deklariert sind). Das sind mindestens die von Dir genannten, können aber noch mehr sein (wenn die Implementierung sich auf diese Weise irgendwas vereinfachen wollte). Die Unixe haben für alle Funktionen, die vom API her nicht thread-safe sind (das sind die von Dir genannten) mittlerweile alternative Funktionen implementiert, die den Kontext als Parameter übergeben bekommen, so daß der Aufrufer für dessen Zwischenspeicherung zuständig ist. Die Konvention dabei ist, an den normalen Funktionsnamen ein _r anzuhängen. Für das Beispiel strtok() ist das dann also strtok_r(). avr-libc folgt dieser Konvention übrigens, wobei außer strtok_r() derzeit nur noch random_r() und rand_r() implementiert sind.
>> "thread-unsaven" hat überhaupt nichts mit reentrant zu tun.
Das sehe ich immer noch anders. Wenn eine Routine "reentrant" ist,
dann ist sie automatisch auch "tread-safe".
Gruß, Frank
> Da aber ein Zeiger auf den gefundenen Teilstring geliefert wird, > braucht man sich diesen ja nur zu merken um danach die nächste Suche > zu beginnen und alles ist in Butter. Also der Normale Aufruf von strtok sieht doch so aus, dass der zweite u. die folgenden Aufrufe mit NULL und nicht mit dem Teilstring durchgeführt wird und damit funktioniert es nicht thread-save. Es sollte thread-save sein, wenn es immer mit dem Teilstring (ungleich NULL) aufgerufen wird. Reentrant ist im Wesentlichen allgemeiner als thread-save, denn reentrant bedeutet, dass die Funktion auch in einem Speicherbereich mehrfach laufen kann, z. B. auf 4 CPUs des Rechnerns gleichzeitig. Thread-save heißt ja nur, dass verschiedene Threads die Funktion auch in einem Speicherbereich ohne Seiteneffekt benutzen können; es sagt nichts darüber aus, ob die Funktion auch reentrant ist.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.