Kann mir jemand erklären, wo genau setjmp()
und welche longjmp()
Funktionen in der eingebetteten Programmierung praktisch eingesetzt werden können? Ich weiß, dass diese zur Fehlerbehandlung dienen. Aber ich würde gerne einige Anwendungsfälle kennen.
96
longjmp()
, um aus einem Signalhandler auszusteigen, insbesondere Dinge wie aBUS ERROR
. Dieses Signal kann normalerweise nicht neu gestartet werden. Eine eingebettete Anwendung möchte diesen Fall möglicherweise aus Sicherheitsgründen und für einen robusten Betrieb behandeln.setjmp
zwischen BSD und Linux finden „Timing setjmp und die Freude an der Standards“ , die unter Verwendung schlägtsigsetjmp
.Antworten:
Fehlerbehandlung
Angenommen , es ist ein Fehler , tief unten in einer Funktion verschachtelt in vielen anderen Funktionen und Fehlerbehandlung macht nur Sinn in der obersten Ebene Funktion.
Es wäre sehr mühsam und umständlich, wenn alle dazwischen liegenden Funktionen normal zurückkehren und Rückgabewerte oder eine globale Fehlervariable auswerten müssten, um festzustellen, dass eine weitere Verarbeitung keinen Sinn ergibt oder sogar schlecht wäre.
In dieser Situation ist setjmp / longjmp sinnvoll. Diese Situationen ähneln Situationen, in denen Ausnahmen in anderen Sprachen (C ++, Java) sinnvoll sind.
Coroutinen
Neben der Fehlerbehandlung kann ich mir auch eine andere Situation vorstellen, in der Sie setjmp / longjmp in C benötigen:
Dies ist der Fall, wenn Sie Coroutinen implementieren müssen .
Hier ist ein kleines Demo-Beispiel. Ich hoffe, es erfüllt die Anfrage von Sivaprasad Palas nach einem Beispielcode und beantwortet die Frage von TheBlastOne, wie setjmp / longjmp die Implementierung von Korroutinen unterstützt (soweit ich sehe, basiert es nicht auf einem nicht standardmäßigen oder neuen Verhalten).
BEARBEITEN:
Es könnte sein, dass es tatsächlich ein undefiniertes Verhalten
longjmp
ist, den Callstack herunterzufahren (siehe Kommentar von MikeMB; obwohl ich noch keine Gelegenheit hatte, dies zu überprüfen).Die folgende Abbildung zeigt den Ausführungsfluss:
Warnhinweis Beachten Sie bei der Verwendung von setjmp / longjmp, dass sie sich auf die Gültigkeit lokaler Variablen auswirken, die häufig nicht berücksichtigt werden.
Vgl. meine Frage zu diesem Thema .
quelle
setjmp
vor Ihnenlongjmp
. Dies ist nicht Standard.routineA
undroutineB
verwenden Sie den gleichen Stapel, funktioniert es nur für sehr primitive Coroutinen. WennroutineA
Anrufe ein verschachtelt tiefroutineC
auf nach dem ersten AufrufroutineB
und dieserroutineC
läuftroutineB
als Koroutine, dannroutineB
könnte auch die Rückkehr Stack zerstört (nicht nur lokale Variablen) vonroutineC
. Wenn Sie also keinen exklusiven Stapel zuweisen (durchalloca()
nach dem AufrufrountineB
?), Werden Sie ernsthafte Probleme mit diesem Beispiel bekommen, wenn es als Rezept verwendet wird.Die Theorie besagt, dass Sie sie zur Fehlerbehandlung verwenden können, um aus der tief verschachtelten Aufrufkette herauszuspringen, ohne sich mit der Behandlung von Fehlern in jeder Funktion in der Kette befassen zu müssen.
Wie jede kluge Theorie fällt dies auseinander, wenn man der Realität begegnet. Ihre Zwischenfunktionen weisen Speicher zu, greifen zu Sperren, öffnen Dateien und erledigen alle möglichen Dinge, die bereinigt werden müssen. In der Praxis sind
setjmp
/longjmp
also normalerweise eine schlechte Idee, außer unter sehr begrenzten Umständen, unter denen Sie die vollständige Kontrolle über Ihre Umgebung haben (einige eingebettete Plattformen).Nach meiner Erfahrung ist Ihr Programm in den meisten Fällen, wenn Sie glauben, dass die Verwendung von
setjmp
/longjmp
funktionieren würde, klar und einfach genug, dass jeder Zwischenfunktionsaufruf in der Aufrufkette eine Fehlerbehandlung durchführen kann, oder es ist so chaotisch und unmöglich zu beheben, dass Sie es tun sollten,exit
wenn Sie es tun auf den Fehler stoßen.quelle
libjpeg
. Wie in C ++ benötigen die meisten Sammlungen von C-Routinen einestruct *
, um etwas als Kollektiv zu bearbeiten. Anstatt die Speicherzuordnungen Ihrer Zwischenfunktionen als Lokale zu speichern, können sie in der Struktur gespeichert werden. Dadurch kann einlongjmp()
Handler den Speicher freigeben. Außerdem gibt es hier nicht so viele gestrahlte Ausnahmetabellen, dass alle C ++ - Compiler noch 20 Jahre später generieren.Like every clever theory this falls apart when meeting reality.
In der Tat machen temporäre Zuweisungen und dergleichenlongjmp()
schwierig, da Sie dannsetjmp()
mehrmals im Aufrufstapel arbeiten müssen (einmal für jede Funktion, die vor dem Beenden eine Art Bereinigung durchführen muss, die dann "die Ausnahme erneut auslösen" muss). durchlongjmp()
den Kontext, den es ursprünglich erhalten hatte). Es wird noch schlimmer, wenn diese Ressourcen nach dem geändert werdensetjmp()
, da Sie sie deklarieren müssenvolatile
, um zu verhindern, dasslongjmp()
sie überlastet werden.Die Kombination von
setjmp
undlongjmp
ist "Superstärkegoto
". Mit äußerster Sorgfalt verwenden. Wie andere bereits erklärt haben, ist alongjmp
jedoch sehr nützlich, um aus einer unangenehmen Fehlersituation herauszukommen, wenn Sieget me back to the beginning
schnell möchten , anstatt eine Fehlermeldung für 18 Funktionsebenen zurückzusenden.Genauso wie
goto
, aber schlimmer, müssen Sie WIRKLICH vorsichtig sein, wie Sie dies verwenden. Alongjmp
bringt Sie nur zurück zum Anfang des Codes. Es wirkt sich nicht auf alle anderen Zustände aus, die sich zwischen demsetjmp
und dem Zurückkehren zu demsetjmp
Ausgangspunkt geändert haben . Zuweisungen, Sperren, halb initialisierte Datenstrukturen usw. werden also weiterhin zugewiesen, gesperrt und halb initialisiert, wenn Sie zu dem Ort zurückkehren, an dem siesetjmp
aufgerufen wurden. Dies bedeutet, dass Sie sich wirklich um die Orte kümmern müssen, an denen Sie dies tun, damit es WIRKLICH in Ordnung ist, anzurufen,longjmp
ohne MEHR Probleme zu verursachen. Wenn Sie als Nächstes "neu starten" [möglicherweise nach dem Speichern einer Meldung über den Fehler] - in einem eingebetteten System, in dem Sie beispielsweise festgestellt haben, dass sich die Hardware in einem schlechten Zustand befindet, ist dies in Ordnung.Ich habe auch gesehen
setjmp
/longjmp
verwendet, um sehr grundlegende Threading-Mechanismen bereitzustellen. Aber das ist ein ziemlich spezieller Fall - und definitiv nicht, wie "Standard" -Threads funktionieren.Bearbeiten: Man könnte natürlich Code hinzufügen, um "mit der Bereinigung umzugehen", genauso wie C ++ die Ausnahmepunkte im kompilierten Code speichert und dann weiß, was eine Ausnahme gegeben hat und was bereinigt werden muss. Dies würde eine Art Funktionszeigertabelle und das Speichern von "Wenn wir hier von unten herausspringen, rufen Sie diese Funktion mit diesem Argument auf" beinhalten. Etwas wie das:
Mit diesem System können Sie "die Ausnahmebehandlung wie C ++ abschließen". Aber es ist ziemlich chaotisch und setzt voraus, dass der Code gut geschrieben ist.
quelle
setjmp
jede Initialisierung a la C ++ schützen, und erwähnenswert ist, dass die Verwendung für das Threading nicht Standard ist.Da Sie Embedded erwähnen, ist es meines Erachtens erwähnenswert, einen Nichtanwendungsfall zu erwähnen : Wenn Ihr Codierungsstandard dies verbietet. Zum Beispiel MISRA (MISRA-C: 2004: Regel 20.7) und JFS (AV-Regel 20): "Das Makro setjmp und die Funktion longjmp dürfen nicht verwendet werden."
quelle
setjmp
undlongjmp
kann beim Testen von Einheiten sehr nützlich sein.Angenommen, wir möchten das folgende Modul testen:
Wenn die zu testende Funktion eine andere Funktion aufruft, können Sie normalerweise eine Stub-Funktion zum Aufrufen deklarieren, die nachahmt, was die eigentliche Funktion zum Testen bestimmter Flows tut. In diesem Fall ruft jedoch die Funktion auf,
exit
die nicht zurückkehrt. Der Stub muss dieses Verhalten irgendwie emulieren.setjmp
undlongjmp
kann das für dich tun.Um diese Funktion zu testen, können wir das folgende Testprogramm erstellen:
In diesem Beispiel verwenden Sie,
setjmp
bevorexit
Sie dielongjmp
zu testende Funktion eingeben , und rufen dann im Stubbed auf, um direkt zu Ihrem Testfall zurückzukehren.Beachten Sie auch, dass die neu definierte
exit
Variable eine spezielle Variable enthält, die überprüft, ob Sie das Programm tatsächlich beenden möchten, und dazu aufruft_exit
. Wenn Sie dies nicht tun, wird Ihr Testprogramm möglicherweise nicht sauber beendet.quelle
Ich habe eine schriftliche Java-ähnliche Ausnahmebehandlung in C unter Verwendung
setjmp()
,longjmp()
und Systemfunktionen. Es fängt benutzerdefinierte Ausnahmen ab, signalisiert aber auch wieSIGSEGV
. Es bietet eine unendliche Verschachtelung von Ausnahmebehandlungsblöcken, die über Funktionsaufrufe hinweg funktioniert, und unterstützt die beiden am häufigsten verwendeten Threading-Implementierungen. Sie können eine Baumhierarchie von Ausnahmeklassen definieren, die über eine Vererbung zur Verbindungszeit verfügen, und diecatch
Anweisung durchsucht diesen Baum, um festzustellen, ob er abgefangen oder weitergegeben werden muss.Hier ist ein Beispiel dafür, wie Code damit aussieht:
Und hier ist ein Teil der Include-Datei, der viel Logik enthält:
Es gibt auch ein C-Modul, das die Logik für die Signalverarbeitung und die Buchhaltung enthält.
Die Implementierung war äußerst schwierig. Ich kann Ihnen sagen, und ich habe fast aufgehört. Ich habe mich wirklich bemüht, Java so nahe wie möglich zu kommen. Ich fand es überraschend, wie weit ich mit nur C gekommen bin.
Rufen Sie mich an, wenn Sie interessiert sind.
quelle
main()
bei einer nicht erfassten Ausnahme beendet wird. Bitte stimmen Sie dieser Antwort zu :-)Progagation
Abschnitt in der README-Datei. Ich habe meinen Code vom April 1999 auf GitHub gepostet (siehe Link in der bearbeiteten Antwort). Guck mal; Es war eine harte Nuss zu knacken. Wäre schön zu hören, was du denkst.Zweifellos ist die wichtigste Verwendung von setjmp / longjmp, dass es sich um einen "nicht-lokalen Goto-Jump" handelt. Der Befehl Springen (und es gibt seltene Fälle, in denen Sie goto over für und while-Schleifen verwenden müssen) wird im selben Bereich am sichersten verwendet. Wenn Sie mit goto über Bereiche (oder über die automatische Zuweisung) springen, wird der Stapel Ihres Programms höchstwahrscheinlich beschädigt. setjmp / longjmp vermeidet dies, indem die Stapelinformationen an der Stelle gespeichert werden, zu der Sie springen möchten. Wenn Sie dann springen, werden diese Stapelinformationen geladen. Ohne diese Funktion müssten sich C-Programmierer höchstwahrscheinlich an die Assembly-Programmierung wenden, um Probleme zu lösen, die nur mit setjmp / longjmp gelöst werden können. Gott sei Dank existiert es. Alles in der C-Bibliothek ist extrem wichtig. Sie werden wissen, wann Sie es brauchen.
quelle