Bin auf der Suche nach einer re-entranten Implementierung von printf. Google brachte bereits einige Ergebnisse. http://www.sparetimelabs.com/tinyprintf/index.html http://www.efgh.com/software/gprintf.htm Hat jemand bereits Erfahrung mit diesen Implementierungen gemacht? Kennt jemand nocht weitere? Wichtig ist mir in erster Linie die Performance - weniger die Codegröße. Das ganze soll auf einem ARM9 laufen. Bin gespannt auf die Antworten
Klaus B. schrieb: > Bin auf der Suche nach einer re-entranten Implementierung von printf. Willst Du etwa sowas erreichen: "Text 1", "Text 2" ergibt: "TexText 2t 1" Ich hab sowas mal versehentlich gemacht, printf im Main und im Interrupt. printf sollte sich tunlichst nicht selber unterbrechen. Peter
Genau das habe ich mit snprintf aus der DLIB von IAR erreicht ;-) Ich verstehe ehrlich gesagt nicht weshalb printf nicht reentrant implementiert ist. Ich verwende ein Multitaskingsystem wo einige Male ..printf verwendet wird. Jetz bin ich eben auf der Suche nach ner thread sicheren Implementierung.
Klaus B. schrieb: > Genau das habe ich mit snprintf aus der DLIB von IAR erreicht ;-) > Ich verstehe ehrlich gesagt nicht weshalb printf nicht reentrant > implementiert ist. Steht zumindest bei AVR in der Doku zu avrlibc: printf(), printf_P(), vprintf(), vprintf_P(), puts(), puts_P() Alters flags and character count in global FILE stdout. Use only in one thread. Or if returned character count is unimportant, do not use the *_P versions. Note: Formatting to a string output, e.g. sprintf(), sprintf_P(), snprintf(), snprintf_P(), vsprintf(), vsprintf_P(), vsnprintf(), vsnprintf_P(), is thread safe. The formatted string could then be followed by an fwrite() which simply calls the lower layer to send the string. Dementsprechend auch die Abhilfe: sprintf() verwenden und den String anderweitig zu stdout schaufeln. Könnte hier doch ähnlich sein?
Peter Dannegger schrieb: > "Text 1", "Text 2" ergibt: > "TexText 2t 1" Die beiden Texte aus unterschiedlichen Threads? Das ist ja eigentlich normales Verhalten, und hat mit den üblichen reentrant-Problemen eigentlich nichts zu tun. Wenn man eine Resource (stdout) von 2 Threads aus nutzt, würde ich eigentlich nichts anderes erwarten. Abhilfe wäre ein Thread, der durch Pipes o.ä. von den den threads mit der Ausgabe gefüttert wird und z.B. anhand von \n synchronisiert.
Hm, das Problem ist, ich verwende snprintf und hab trotzdem kryptische Ausgaben :-) Ich bin kurz davor jeden Aufruf von snprintf u.ä. mit ner Mutex zu schützen.
Klaus B. schrieb: > Ich verstehe ehrlich gesagt nicht weshalb printf nicht reentrant > implementiert ist. Das sagt einem schon die Logik. Informationskanäle dürfen generell nicht unterbrochen werden. Dein Handy-Gespräch darf auch nicht unterbrochen werden, Ein Datei schreiben auf die Festplatte nicht mit ner anderen Datei gemischt werden usw. Informationen müssen immer komplett gesendet werden, sonst verlieren sie ihren Sinn. Sie müssen ihre Reihenfolge beibehalten. Eine Ausnahme sind Fernsehsendungen, die dürfen unterbrochen werden (Werbung). Peter
Klaus Wachtler schrieb: > Wenn man eine Resource (stdout) von 2 Threads aus nutzt, würde > ich eigentlich nichts anderes erwarten. Das ist klar. Nur hab ich den Eindruck dass sprintf ect mit irgendwelchen static oder globale Variablen / Buffern arbeiten :-( z.B. Führt der folgender Code
1 | char buffer[32]; |
2 | sprintf(buffer, "%s %s", "Text 1", "Text 2"); |
zu folgender Ausgabe: "TexText 2t 1". :-( Ich habe das Gefühl, dass wenn während der sprintf Ausführung ein Kontextwechsel stattfindet und sprintf innerhalb dieses Kontextes auch verwendet wird, die unterbrochene Ausführung von sprintf zu der Fehlerhaften Ausgabe führt. (Das war jetzt im grunde die definition von reentrant)
Peter Dannegger schrieb: > Das sagt einem schon die Logik. Informationskanäle dürfen generell nicht > unterbrochen werden. Hier geb ich dir natürlich Recht. Eine Ausgabe wie "TexText 2t 1" auf stdout ist erklärbar. Der char buffer[32] jedoch gehört exklusiv einer Task! Peter Dannegger schrieb: > Eine Ausnahme sind Fernsehsendungen, die dürfen unterbrochen werden > (Werbung). :-) Sehr schön.
Klaus B. schrieb: > Ich verwende ein Multitaskingsystem wo einige Male ..printf verwendet > wird. Das Problem hat man oft, daß verschiedene Tasks etwas ausgeben sollen. Ich teile dazu das LCD in Bereiche auf, jede Task hat ihr eigenes Fenster, wo sie nach belieben reinschreiben darf. Sie tut das aber nicht direkt, sondern schreibt in einen SRAM-Bereich. Und dieser SRAM wird dann zyklisch an das LCD ausgegeben. Jedes sprintf hat also seinen eigenen Pointer, wo es reinschreiben darf. Peter
Peter Dannegger schrieb: > Jedes sprintf hat also seinen eigenen Pointer, wo es reinschreiben darf. Genau so mache ich das auch. Klaus B. schrieb: > char buffer[32]; > sprintf(buffer, "%s %s", "Text 1", "Text 2"); buffer ist nicht global, sondern gehört genau einer Task (keine Sorge, buffer liegt nicht auf dem Stack). Ich vermute sprintf verwendet irgendwelche globale bzw static Variablen (sowas wie errno). Ich befürchte dass mit folgendem abgändertem Code die Probleme weg sind.
1 | int save_sprintf ( char * str, const char * format, ... ) |
2 | {
|
3 | /* Pseudocode:
|
4 | |
5 | sema_take();
|
6 | sprintf();
|
7 | sema_give();
|
8 | */
|
9 | }
|
10 | |
11 | char buffer[32]; |
12 | save_sprintf(buffer, "%s %s", "Text 1", "Text 2"); |
Ich möchte aber vermeiden, bei jedem sprintf auch noch ne Mutex zu nehmen und wieder geben. Deshalb suche ich eine reentrante implementierung von sprintf :-)
Hallo Klaus, ich habe zwar keine Ahnung von ARM9, aber Probleme mit Reentranz sind nach meiner Erfahrung oft eher nicht reproduzierbar (das macht sie ja gerade so tückisch!). Wenn Du regelmäßig geshredderte Ausgaben hast, dann ist es möglich, daß Du ein ganz anderes Problem hast (falscher Formatstring, falsche Datenübergabe, Zeigerfehler, überschriebene Daten). Zu Deinem Beispiel von 21:11: Wer erzeugt denn Interrupts, die einen Taskwechsel erzeugen können und wer behandelt die? Ist vielleicht ein Interrupthandler nicht sauber beim Speichern und Restore des Zustands der CPU? Oder versucht die Library, den Aufruf zu parallelisieren und versemmelt es? Versehentlich Single-threaded-Version der Library gelinked?
Matthias H. schrieb: > Versehentlich Single-threaded-Version der Library gelinked? So weit ich weiß liefert IAR keine multithread versionen der library mit. Werd ich morgen überprüfen. Im Interrupt verwende ich kein printf o.ä. Allerdings in unterschielichen Tasks. Das Problem ist in der Tat sehr schwer zu reproduzieren. Ich kann allerdings beobachten, dass dieses Problem umso häufiger auftritt, je höher die Systemlast (und der damit verbundenen häufigeren Kontextwechseln) ist, weshalb ich auf ein reentrancy Problem tippe. Ich verstehe allerdings nach wie vor nicht, weshalb sprintf nicht reentrant ist. Schließlich wird ein "Arbeitsbuffer" mit übergeben. Ich kann mir nicht vorstellen welche Art von globalen Variablen bei sprintf benötigt werden, die solch einen Effekt verursachen. (Errno ist global, wird aber meines Wissens von sprintf nicht gesetzt!? Irgendwelche Zeiger zum positionieren im String? Indices?) Ich werd es auch nicht rausfinden, da ich bezweifle dass IAR die sourcen Ihrer stdio lib herausrücken ;-) Matthias H. schrieb: > Wenn Du regelmäßig geshredderte Ausgaben hast, > dann ist es möglich, daß Du ein ganz anderes Problem hast (falscher > Formatstring, falsche Datenübergabe, Zeigerfehler, überschriebene > Daten). Eher unregelmäßig, also nicht reproduzierbar...
Klaus B. schrieb: > Ich verstehe allerdings nach wie vor nicht, weshalb sprintf nicht > reentrant ist. Ich sehe auch keinen zwingenden Grund.
Schreib mal ein Programm ohne sprintf und eins mit sprintf und vergleiche die SRAM-Belegung. Peter
Klaus B. schrieb: > Errno ist global, > wird aber meines Wissens von sprintf nicht gesetzt!? Stimmt.
>Ich kann mir nicht vorstellen welche Art von globalen >Variablen bei sprintf benötigt werden, die solch einen >Effekt verursachen. Eher lokale Variablen, und damit hast du die Arschkarte gezogen wenn du sprintf() nicht atomar aufrufst. sprintf() wird ja nicht als Kopie verwendet sondern als eine Funktion im Speicher. sprintf() in mehrere Tasks zu legen ist genau das gleiche wie sprintf() in einem oder mehreren Interrupts aufzurufen.
Diese Variante funktioniert ohne statischen Buffer, http://www.menie.org/georges/embedded/printf-stdarg.c Das Problem, daß der Interrupt das andere Printf unterbricht, und eine Zeile inmitten deinem anderen Autput hast, wirst du trotzdem haben.
holger schrieb: > sprintf() > wird ja nicht als Kopie verwendet sondern als eine Funktion > im Speicher. Du bist mir hoffentlich nicht böse, wenn ich davon kein Wort verstanden habe?
chris. schrieb: > Das Problem, daß der Interrupt das andere Printf unterbricht, und > eine Zeile inmitten deinem anderen Autput hast, wirst du trotzdem haben. Das wird er nicht haben, weil er ja nicht printf, sondern s(n)printf benutzt.
>> sprintf() >> wird ja nicht als Kopie verwendet sondern als eine Funktion >> im Speicher. >Du bist mir hoffentlich nicht böse, wenn ich davon kein Wort >verstanden habe? Nein. Ach komm Klaus das hast du verstanden. Was er nicht gesagt hat ist wie sein Programm auf dem ARM aussieht. Auf einem ARM9 kann man ja z.B. Linux laufen lassen. Da kann man einzelne Programme kompilieren und jedes davon benutzt sprintf(). Jedes Programm hätte dann eine Kopie von sprintf() und alles ist gut. Wenn er nur ein Programm benutzt und sprintf() dann in mehrere Tasks legt dann gibt es auch nur ein sprintf().
Jetzt (glaube ich) weiß ich, was du meinst. Es geht dir wohl nicht um die Funktion (als Code irgendwo), sondern um ihre Variablen. Jedes halbwegs moderne OS, sogar Windows, nutzt Code mehrfach. Und zwar quer über alle Prozesse und Threads, solange die Funktionen in einem shared object bzw. einer DLL liegen. Das macht auch keine Probleme - alleine deshalb, weil auf den Code ja nur lesend zugegriffen wird. Trotzdem bekommt die Funktion in jedem Thread und in jedem Prozess entsprechend viele Instanzen seiner lokalen Variablen (automatische Variablen, die auf dem Stack). Auch das klappt (auch wenn es keine "Kopien" voneinander sind). Schwierig wird es, wenn eine Funktion globale Variablen nutzt oder lokale statische. Dann tritt der Unterschied zwischen Prozessen und Threads auf, den du vrmtl. meinst. Hat man mehrere Prozesse mit je einem Thread, dann geht alles gut, weil die statischen Variablen einmal je Prozess existieren. Bei mehreren Threads in einem Prozess kneift es dann. Und genau das ist es, was nicht rentrante Funktionen ausmacht: sie haben globale oder statische Variablen. Soweit nichts neues. Die Frage ist jetzt, wieso sprintf solchen statischen Speicher braucht. Dafür gibt es keinen mir ersichtlichen Grund. Bspw. strtok kommt gemäß seiner Definition nicht ohne aus, weil es sich von Aufruf zu Aufruf etwas merken muß. sprintf sollte es aber eigentlich schaffen, wenn nicht jemand Quatsch programmiert hat.
Klaus Wachtler schrieb: > wenn nicht jemand Quatsch programmiert hat. Ich hab gehört das kommt gar nicht so selten vor wie man denkt :-)
holger schrieb: > Nein. Ach komm Klaus das hast du verstanden. Wenn ich ehrlich bin, nein :-) Lokale Variablen landen schließlich auf dem Stack. Jede Task hat ihren eigenen Stack. Wo sollte also das Problem sein? Klaus Wachtler schrieb: > Die Frage ist jetzt, wieso sprintf solchen statischen > Speicher braucht. Dafür gibt es keinen mir ersichtlichen > Grund. Bspw. strtok kommt gemäß seiner Definition nicht > ohne aus, weil es sich von Aufruf zu Aufruf etwas merken > muß. > sprintf sollte es aber eigentlich schaffen, wenn nicht > jemand Quatsch programmiert hat. Ja genau, jetzt sind wir beeinander :-) chris. schrieb: > Diese Variante funktioniert ohne statischen Buffer, > http://www.menie.org/georges/embedded/printf-stdarg.c Werde ich mir gleich mal ansehen, danke.
Hallo Klaus, bzgl. der Interrupts meinte ich nicht einen Aufruf von printf, sondern daß vielleicht ein Interrupthandler entweder nicht alle von printf benutzten Register sichert oder wild im Speicher herumschreibt. Speicherkorruption könnte theoretisch auch allgemein eine Ursache für so ein Verhalten sein, also daß vielleicht ein möglicherweise völlig anderer Programmteil den von der Implementation von printf benutzten Speicher kaputtschreibt.
Matthias H. schrieb: > sondern > daß vielleicht ein Interrupthandler entweder nicht alle von printf > benutzten Register sichert oder wild im Speicher herumschreibt. ein Interrupthandler muss alle Register wieder herstellen die er selber verwendet sonst könnte man kaum sinnvoll programmieren, ausser man reserviert Register nur für die Interupts aber das ken ich nur von ASM programmen.
Klaus Wachtler schrieb: > Die Frage ist jetzt, wieso sprintf solchen statischen > Speicher braucht. Dafür gibt es keinen mir ersichtlichen > Grund. Den Grund stelle ich mir ganz einfach vor. Bei einem Mikroporozessor-Programm (kein Multithreading/-tasking) geht alles nacheinander. Printf und Konsorten haben einen(!) lokalen Zielzeiger. printf wird, abgesehen von INT, komplett bearbeitet. Damit spart man Speicher. Aus diesem Grund kann printf auch nicht alle Ausgabeformate umsetzen. Dafür gibt es dann andere Bibliotheken, die man bei Bedarf verwendet. Um eine threadsichere Ausgabe zu haben, muss eine andere Bibliothek her, oder man schreibt sich sein printf selber. In diesem Fall mit alle Variablen auf dem Stack (global oder threadlokal). Für mich also einfach eine Frage der effizienz.
Peter schrieb: > ein Interrupthandler muss alle Register wieder herstellen die er selber Du meinst sollte > verwendet sonst könnte man kaum sinnvoll programmieren, Manche machen das nicht und wundern sich später.
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.