Warum ist volatile
in C benötigt? Wofür wird es verwendet? Was wird es tun?
Antworten
Zu viele Anzeigen?volatile
weist den Compiler an, nichts zu optimieren, was mit der volatile
variabel.
Es gibt mindestens drei häufige Gründe für die Verwendung, die alle mit Situationen zu tun haben, in denen sich der Wert der Variablen ohne Aktion des sichtbaren Codes ändern kann: Wenn Sie eine Schnittstelle zu Hardware haben, die den Wert selbst ändert; wenn ein anderer Thread läuft, der die Variable ebenfalls verwendet; oder wenn es einen Signal-Handler gibt, der den Wert der Variable ändern könnte.
Nehmen wir an, Sie haben ein kleines Stück Hardware, das irgendwo im RAM abgebildet ist und das zwei Adressen hat: einen Befehlsanschluss und einen Datenanschluss:
typedef struct
{
int command;
int data;
int isBusy;
} MyHardwareGadget;
Jetzt wollen Sie einen Befehl senden:
void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isbusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
Sieht einfach aus, kann aber scheitern, weil der Compiler die Reihenfolge, in der Daten und Befehle geschrieben werden, frei ändern kann. Dies würde dazu führen, dass unser kleines Gadget Befehle mit dem vorherigen Datenwert ausgibt. Werfen Sie auch einen Blick auf die wait while busy-Schleife. Diese Schleife wird optimiert. Der Compiler wird versuchen, clever zu sein und den Wert von isBusy
nur einmal und gehen dann in eine Endlosschleife über. Das ist nicht das, was Sie wollen.
Dies kann man umgehen, indem man den Zeiger deklariert gadget
como volatile
. Auf diese Weise ist der Compiler gezwungen, das zu tun, was Sie geschrieben haben. Er kann die Speicherzuweisungen nicht entfernen, er kann keine Variablen in Registern zwischenspeichern und er kann auch die Reihenfolge der Zuweisungen nicht ändern
Dies ist die richtige Version:
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isBusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
volatile
in C entstand eigentlich aus dem Grund, um die Werte der Variablen nicht automatisch zwischenzuspeichern. Sie teilt dem Compiler mit, dass er den Wert dieser Variablen nicht zwischenspeichern soll. Es wird also Code generiert, der den Wert der angegebenen volatile
Variable aus dem Arbeitsspeicher zu entfernen, wenn sie auf diese trifft. Dieser Mechanismus wird verwendet, weil der Wert jederzeit durch das Betriebssystem oder eine Unterbrechung geändert werden kann. Daher ist die Verwendung von volatile
wird uns dabei helfen, den Wert jedes Mal aufs Neue zu ermitteln.
Eine weitere Verwendung für volatile
sind Signalhandler. Wenn Sie Code wie diesen haben:
int quit = 0;
while (!quit)
{
/* very small loop which is completely visible to the compiler */
}
Der Compiler darf feststellen, dass der Schleifenkörper nicht die quit
und wandeln Sie die Schleife in eine while (true)
Schleife. Auch wenn die quit
wird im Signalhandler für SIGINT
y SIGTERM
Der Compiler hat keine Möglichkeit, das zu wissen.
Wenn jedoch die quit
Variable wird deklariert volatile
ist der Compiler gezwungen, sie jedes Mal zu laden, da sie an anderer Stelle geändert werden kann. Das ist genau das, was Sie in dieser Situation wollen.
volatile
teilt dem Compiler mit, dass Ihre Variable durch andere Mittel als den Code, der auf sie zugreift, geändert werden kann. z.B. kann es sich um eine I/O-mapped Speicherstelle handeln. Wenn dies in solchen Fällen nicht angegeben wird, können einige Variablenzugriffe optimiert werden, z. B. kann ihr Inhalt in einem Register gehalten werden und die Speicherstelle nicht wieder eingelesen werden.
Siehe diesen Artikel von Andrei Alexandrescu, " volatile - der beste Freund des Multithreading-Programmierers "
El flüchtig Schlüsselwort war erdacht, um Compiler-Optimierungen zu verhindern Optimierungen zu verhindern, die den Code Code bei Vorhandensein bestimmter asynchronen Ereignissen. Zum Beispiel, wenn deklarieren Sie eine primitive Variable als flüchtig ist der Compiler nicht in einem Register zwischenspeichern darf -- eine übliche Optimierung, die verhängnisvoll wäre, wenn diese Variable von mehreren Threads gemeinsam genutzt wird. Daher ist die allgemeine Regel lautet also, wenn Sie Variablen von primitivem Typ haben, die von mehreren zwischen mehreren Threads gemeinsam genutzt werden müssen, deklarieren Sie diese Variablen flüchtig . Aber Sie können viel mehr mit diesem Schlüsselwort noch viel mehr tun: Sie können es verwenden, um Code abzufangen abfangen, der nicht thread-sicher ist, und Sie können zur Kompilierzeit. Dieser Artikel zeigt, wie man das macht; die Lösung beinhaltet einen einfachen intelligenten Zeiger, der auch die Serialisierung von kritische Abschnitte des Codes.
Der Artikel gilt sowohl für C
y C++
.
Siehe auch den Artikel " C++ und die Gefahren der doppelt überprüften Sperre " von Scott Meyers und Andrei Alexandrescu:
Beim Umgang mit einigen Speicherplätzen (z.B. speichergemappte Ports oder Speicher, auf den ISRs [ Interrupt Service Routines ] verweisen) müssen daher einige Optimierungen ausgesetzt werden. volatile existiert, um eine besondere Behandlung für solche Speicherplätze festzulegen, und zwar: (1) der Inhalt einer flüchtigen Variablen ist "instabil" (kann sich auf eine dem Compiler unbekannte Weise ändern), (2) alle Schreibvorgänge auf flüchtige Daten sind "beobachtbar", so dass sie gewissenhaft ausgeführt werden müssen, und (3) alle Operationen auf flüchtige Daten werden in der Reihenfolge ausgeführt, in der sie im Quellcode erscheinen. Die ersten beiden Regeln gewährleisten ein ordnungsgemäßes Lesen und Schreiben. Die letzte Regel ermöglicht die Implementierung von E/A-Protokollen, die Eingabe und Ausgabe mischen. Dies ist informell das, was C und C++'s volatile garantiert.
- See previous answers
- Weitere Antworten anzeigen