Ich bin mit dem Schlüsselwort vertraut Volatile
, das zum Deklarieren von Variablen verwendet wird, die von mehreren Threads in einer Softwareanwendung (im Grunde in einer Multithread-Anwendung) gemeinsam genutzt werden.
Aber warum muss ich eine Variable deklarieren, Volatile
wenn der Code auf einem Arduino-Interrupt ausgeführt wird? Wird der im Interrupt ausgeführte Code auf einem anderen Thread ausgeführt? Ist noch etwas los?
Is the code running in the interrupt executing on different thread
- Nun, irgendwie nur das Gegenteil. Interrupts sind der Mechanismus, die von Betriebssystemprogrammierern zum Implementieren von Threads verwendet wird. Wir müssen alsovolatile
Multithread-Code verwenden, da deren Kontext vom Betriebssystem in einem Interrupt geändert wurde. Wir müssenvolatile
Interrupts einfach verwenden, weil Interrupts in einem anderen Kontext ausgeführt werden.Antworten:
Erstens ist es
volatile
nichtVolatile
. Ich beschreibe diese Konzepte auf meiner Seite über Interrupts. Um jedoch zu vermeiden, dass nur ein Link beantwortet wird, wiederhole ich die relevanten Bits.Was sind "flüchtige" Variablen?
Variablen, die zwischen ISR-Funktionen und normalen Funktionen geteilt werden, sollten deklariert werden
volatile
. Dies teilt dem Compiler mit, dass sich solche Variablen jederzeit ändern können. Daher muss der Compiler die Variable immer dann neu laden, wenn Sie darauf verweisen, anstatt sich auf eine Kopie zu verlassen, die sich möglicherweise in einem Prozessorregister befindet.Zum Beispiel:
Kritische Abschnitte ... Zugriff auf flüchtige Variablen
Es gibt einige subtile Probleme in Bezug auf Variablen, die von Interrupt Service Routines (ISRs) und dem Hauptcode (dh dem Code, der nicht in einem ISR enthalten ist) gemeinsam genutzt werden.
Da ein ISR jederzeit ausgelöst werden kann, wenn Interrupts aktiviert sind, müssen Sie beim Zugriff auf solche gemeinsam genutzten Variablen vorsichtig sein, da diese möglicherweise in dem Moment aktualisiert werden, in dem Sie auf sie zugreifen.
Erstens ... wann verwenden Sie "flüchtige" Variablen?
Eine Variable sollte nur dann als flüchtig markiert werden, wenn sie sowohl innerhalb als auch außerhalb eines ISR verwendet wird.
z.B.
Das Markieren einer Variablen als flüchtig weist den Compiler an, den Inhalt der Variablen nicht in einem Prozessorregister "zwischenzuspeichern", sondern ihn bei Bedarf immer aus dem Speicher zu lesen. Dies kann die Verarbeitung verlangsamen, weshalb Sie nicht jede Variable flüchtig machen, wenn sie nicht benötigt wird.
Atomarer Zugang
Betrachten Sie diesen Code:
Der Code, für den generiert wird
count++
(addiere 1, um zu zählen), lautet wie folgt:(Beachten Sie, dass 1 durch Subtrahieren von -1 addiert wird.)
Hier gibt es zwei Gefahrenpunkte, wie markiert. Wenn der Interrupt nach dem lds (Laderegister 24 mit der Variablen
count
), aber vor den sts (zurück in die Variablecount
speichern) ausgelöst wird, kann die Variable vom ISR (TIMER1_OVF_vect) geändert werden, diese Änderung geht jedoch jetzt verloren , da die Variable im Register wurde stattdessen verwendet.Wir müssen die gemeinsam genutzte Variable schützen, indem wir Interrupts wie folgt kurz ausschalten:
Jetzt erfolgt die Aktualisierung von
count
"atomar" ... das heißt, sie kann nicht unterbrochen werden.Multi-Byte-Variablen
Lassen Sie uns
count
eine 2-Byte-Variable erstellen und das andere Problem sehen:OK, wir ändern uns nicht
count
mehr. Gibt es also immer noch ein Problem? Leider schon. Schauen wir uns den generierten Code für die "if" -Anweisung an:Stellen Sie sich vor,
count
das wäre 0xFFFF und würde gleich wieder auf Null "umlaufen". Wir laden 0xFF in ein Register, aber bevor wir das zweite 0xFF laden, ändert sich die Variable in 0x00. Jetzt denken wir,count
ist 0x00FF, was weder der Wert ist, den es vorher (0xFFFF) noch jetzt (0x0000) hatte.Also müssen wir den Zugriff auf die gemeinsam genutzte Variable wieder wie folgt "schützen":
Was ist, wenn Sie nicht sicher sind, ob Interrupts aktiviert oder deaktiviert sind?
Hier gibt es ein letztes "Gotcha". Was ist, wenn Interrupts bereits ausgeschaltet sind? Dann ist es eine schlechte Idee, sie danach wieder einzuschalten.
In diesem Fall müssen Sie das Prozessorstatusregister wie folgt speichern:
Dieser "sichere" Code speichert den aktuellen Status von Interrupts, schaltet sie aus (sie sind möglicherweise bereits ausgeschaltet), verwandelt die gemeinsam genutzte Variable in eine temporäre Variable, schaltet Interrupts wieder ein - wenn sie bei der Eingabe der Funktion aktiviert waren - und kehrt dann zurück die Kopie der gemeinsam genutzten Variablen.
Zusammenfassung
Code scheint "zu funktionieren", auch wenn Sie die oben genannten Vorsichtsmaßnahmen nicht treffen. Dies liegt daran, dass die Wahrscheinlichkeit, dass der Interrupt genau zum falschen Zeitpunkt auftritt, relativ gering ist (möglicherweise 1 zu 100, abhängig von der Codegröße). Aber früher oder später wird es im falschen Moment passieren und Ihr Code wird entweder abstürzen oder gelegentlich die falschen Ergebnisse zurückgeben.
Achten Sie daher für zuverlässigen Code auf den Schutz des Zugriffs auf gemeinsam genutzte Variablen.
quelle
volatile
Weist den Compiler lediglich an, Variablen nicht zu optimieren und in Prozessorregistern zu behalten. Es hat keine weiteren Nebenwirkungen (abgesehen davon, dass der Compiler weiß, dass sich diese Variablen jederzeit ändern können, werden flüchtige Variablen niemals optimiert, weil sie "nicht verwendet" werden). Also ja, wie oben dargestellt, "schützen" Sie den Zugriff auf Multi-Byte-Variablen, indem Sie Interrupts ausschalten.volatile
hat nichts mit Multithreading an sich zu tun, und es scheint zu den am häufigsten verwendeten Keywords in diesem Bereich zu gehören. Es ist niemals ein Ersatz für eine ordnungsgemäße Synchronisation.Ruft gemäß Standard C das
volatile
meist implementierungsdefinierte Verhalten auf, bei dem der Compiler angewiesen wird, die Zugriffszugriffe auf den Speicher, in dem sich eine Variable befindet, nicht zu optimieren, selbst wenn der Compiler der Meinung ist, dass es keine andere geben kann.Dies kann in den folgenden Situationen nützlich sein, in denen der Compiler nicht weiß, dass externe Mittel eine Variable ändern können:
quelle