Die ganze Wahrheit über RTOS von Colin Walls. Artikel 5. Aufgabeninteraktion und Synchronisation



In früheren Artikeln haben wir das Multitasking-Modell untersucht und festgestellt, dass jede Aufgabe ein quasi unabhängiges Programm ist. Obwohl Aufgaben in eingebetteten Systemen ein gewisses Maß an Unabhängigkeit aufweisen, bedeutet dies nicht, dass sie sich nicht „kennen“. Einige Aufgaben sind wirklich von anderen isoliert, aber Interaktion und Synchronisation zwischen ihnen sind eine häufige Anforderung. Dieser Mechanismus ist eine der Schlüsselfunktionen des RTOS. Der Funktionsumfang kann je nach RTOS variieren. In diesem Artikel werden daher öffentlich verfügbare Optionen betrachtet.

Frühere Artikel in der Reihe:
Artikel 4. Aufgaben, Kontextwechsel und Interrupts
Artikel 3. Aufgaben und Planung
Artikel 2. RTOS: Struktur und Echtzeitmodus
Artikel 1. RTOS: Einführung.

Funktionsbereich


Es gibt drei Modelle für die Interaktion und Synchronisation zwischen Aufgaben:

  • Dienste sind an Aufgaben gebunden: RTOS stellt Aufgaben mit Attributen bereit, die eine Interaktion zwischen ihnen ermöglichen. Betrachten Sie Signale als Beispiel.
  • Kernelobjekte sind autonome Kommunikations- oder Synchronisationsmittel. Beispiele: Ereignisflags, Postfächer, Warteschlangen / Kanäle, Semaphoren und Mutexe.
  • Messaging ist ein optimiertes Schema, mit dem Sie mit dem RTOS Nachrichtenobjekte erstellen und von einer Aufgabe auf eine andere oder mehrere übertragen können. Dies ist für die Architektur des Kernels von grundlegender Bedeutung, und daher wird ein solches System als "Messaging-RTOS" bezeichnet.

Die Mechanismen, die für verschiedene Prozesse ideal sind, variieren. Ihre Funktionen können sich überschneiden. Es lohnt sich daher, die Skalierbarkeit dieser Modelle zu berücksichtigen. Wenn für eine Anwendung beispielsweise mehrere Warteschlangen, jedoch nur ein Postfach erforderlich sind, können Sie ein Postfach mit einer Warteschlange für ein Element implementieren. Dieses Objekt ist nicht vollständig optimal, aber der gesamte Code des Postfachs wird nicht in die Anwendung aufgenommen, und daher verringert die Skalierbarkeit die vom RTOS verwendete Speichermenge.

Gemeinsame Variablen oder Speicherbereiche


Ein vereinfachter Ansatz für die Interaktion zwischen Aufgaben ist das Vorhandensein von Variablen oder Speicherbereichen im System, die für alle Aufgaben verfügbar sind. Dieser Ansatz kann trotz seiner Einfachheit auf mehrere Prozesse angewendet werden. Der Zugang muss kontrolliert werden. Wenn die Variable nur ein Byte ist, ist das Schreiben oder Lesen wahrscheinlich eine atomare Operation (d. H. Kontinuierlich), aber es muss vorsichtig vorgegangen werden, wenn der Prozessor andere Operationen an Speicherbytes zulässt, da diese unterbrechbar sein und auftreten können Synchronisationsproblem. Eine Möglichkeit, das Sperren / Entsperren zu implementieren, besteht darin, Interrupts für kurze Zeit zu deaktivieren.

Wenn Sie einen Speicherbereich verwenden, benötigen Sie noch eine Sperre. Sie können das erste Byte als Blockierungsflag verwenden, da die Speicherarchitektur atomaren Zugriff auf dieses Byte bietet. Eine Aufgabe lädt Daten in einen Speicherbereich, setzt ein Flag und wartet darauf, dass sie zurückgesetzt werden. Eine andere Aufgabe besteht darin, auf das Setzen des Flags zu warten, Daten zu lesen und das Flag zurückzusetzen. Die Verwendung der Interrupt-Deaktivierung als Sperre ist weniger sinnvoll, da das Verschieben des gesamten Datenpuffers einige Zeit dauern kann.

Diese Verwendung von gemeinsam genutztem Speicher ähnelt der Implementierung vieler Interprozessorkommunikationen in Mehrkernsystemen. In einigen Fällen sind Hardware-Sperren und / oder Unterbrechungen in die Interprozessor-Schnittstelle des gemeinsam genutzten Speichers integriert.

Signale


Signale sind einer der einfachsten Mechanismen für die Interaktion zwischen Aufgaben, die von herkömmlichen RTOS angeboten werden. Sie enthalten eine Reihe von Bit-Flags (8, 16 oder 32, je nach Anwendung), die einer bestimmten Aufgabe zugeordnet sind.
Das Signalflag (oder mehrere Flags) kann von jeder Task mit der logischen Operation "OR" gesetzt werden. Flag (s) können nur von einer Aufgabe gelesen werden, die ein Signal enthält. Der Lesevorgang ist normalerweise destruktiv, dh die Flags werden ebenfalls zurückgesetzt.
In einigen Systemen werden Signale auf komplexere Weise implementiert, so dass eine vom Task-Eigentümer des Signals zugewiesene Sonderfunktion automatisch ausgeführt wird, wenn Signalflags gesetzt werden. Dadurch muss die Aufgabe die Flags selbst nicht mehr steuern. Dies ähnelt in gewisser Weise einem Interrupt-Handler.

Ereignisflag-Gruppen


Gruppen von Ereignisflags ähneln Signalen darin, dass sie ein bitorientiertes Werkzeug für die Interaktion zwischen Aufgaben sind. Ebenso können sie 8, 16 oder 32 Bit enthalten. Im Gegensatz zu Signalen sind sie unabhängige Kernobjekte und gehören keiner bestimmten Aufgabe an. Jede Aufgabe kann Ereignisflags mit den logischen Operationen "ODER" und "UND" setzen und zurücksetzen. Ebenso kann jede Aufgabe Ereignisflags mit denselben Operationen überprüfen. In vielen RTOS können Sie einen blockierenden API-Aufruf für eine Kombination von Ereignisflags ausführen. Das heißt, die Aufgabe kann angehalten werden, bis eine bestimmte Kombination von Ereignisflags gesetzt ist. Die Option "verbrauchen" ist möglicherweise auch verfügbar, wenn Ereignisflags überprüft werden, wodurch alle Flags zurückgesetzt werden.

Semaphoren


Semaphoren sind unabhängige Kernelobjekte, die für die Ressourcenabrechnung verwendet werden. Es gibt zwei Arten von Semaphoren: binär (kann nur zwei Werte haben) und allgemein (unbegrenzte Anzahl von Werten). Einige Prozessoren unterstützen (atomare) Anweisungen, die die schnelle Implementierung von binären Semaphoren erleichtern. Binäre Semaphore können als allgemeine Semaphoren mit dem Wert 1 implementiert werden.

Jede Aufgabe kann versuchen, ein Semaphor zuzuweisen, um Zugriff auf die Ressource zu erhalten. Wenn der aktuelle Wert des Semaphors größer als 0 ist (das Semaphor ist frei), wird der Zählerwert um 1 reduziert, daher ist die Zuweisung erfolgreich. In vielen Betriebssystemen kann ein Sperrmechanismus verwendet werden, um ein Semaphor zuzuweisen. Dies bedeutet, dass sich die Aufgabe in einem Wartezustand befinden kann, bis das Semaphor von einer anderen Aufgabe freigegeben wird. Jede Aufgabe kann das Semaphor freigeben, und dann erhöht sich der Wert des Semaphors.

Postfächer


Postfächer sind unabhängige Kernelobjekte, mit denen Nachrichten gesendet werden können. Die Größe der Nachricht hängt von der Implementierung ab, ist jedoch normalerweise festgelegt. Typische Nachrichtengrößen sind ein bis vier Elemente von der Größe eines Zeigers. In der Regel wird ein Zeiger auf komplexere Daten über das Postfach gesendet. Einige Kernel implementieren Postfächer so, dass Daten einfach in einer regulären Variablen gespeichert werden und der Kernel den Zugriff darauf steuert. Postfächer können auch als "Austausch" bezeichnet werden, obwohl dieser Name heute nur noch selten vorkommt.

Jede Aufgabe kann Nachrichten an ein Postfach senden, das dann ausgefüllt wird. Wenn eine Aufgabe versucht, eine Nachricht an ein vollständiges Postfach zu senden, erhält sie eine Fehlerantwort. In vielen RTOS können Sie einen Blockierungsmechanismus verwenden, um an die Mailbox zu senden. Dies bedeutet, dass die Aufgabe angehalten wird, bis die Nachricht in der Mailbox gelesen wird. Jede Aufgabe kann Nachrichten aus der Mailbox lesen, danach ist sie leer. Wenn eine Aufgabe versucht, aus einem leeren Postfach zu lesen, erhält sie eine Fehlerantwort. In vielen RTOS können Sie einen blockierenden Aufruf zum Lesen aus einer Mailbox verwenden. Dies bedeutet, dass die Aufgabe angehalten wird, bis eine neue Nachricht im Postfach angezeigt wird.

Einige RTOS unterstützen die Broadcast-Funktion. Auf diese Weise können Sie Nachrichten an alle Aufgaben senden, die derzeit beim Lesen eines bestimmten Postfachs angehalten werden.

Einige RTOS unterstützen Postfächer überhaupt nicht. Stattdessen wird empfohlen, eine Warteschlange mit nur einem Element zu verwenden. Dies ist funktional äquivalent, bringt jedoch zusätzlichen Aufwand für Speicher und Laufzeit mit sich.

Warteschlangen


Warteschlangen sind unabhängige Kernelobjekte, die einen Mechanismus zum Senden von Nachrichten bereitstellen. Sie sind etwas flexibler und komplexer als Postfächer. Die Größe der Nachricht hängt von der Implementierung ab, ist jedoch normalerweise fest und auf das Wort / den Zeiger ausgerichtet.

Jede Aufgabe kann Nachrichten an die Warteschlange senden. Dies kann wiederholt werden, bis die Warteschlange voll ist. Danach führt jeder Sendeversuch zu einem Fehler. Die Länge der Warteschlange wird normalerweise vom Benutzer beim Erstellen oder Konfigurieren des Systems festgelegt. In vielen RTOS können Sie einen Blockierungsmechanismus auf die Warteschlange anwenden. Das heißt, wenn die Warteschlange voll ist, kann die Aufgabe angehalten werden, bis die Nachricht in der Warteschlange von einer anderen Aufgabe gelesen wird. Jede Aufgabe kann Nachrichten aus der Warteschlange lesen. Nachrichten werden in derselben Reihenfolge gelesen, in der sie gesendet wurden (First In - First Out, FIFO). Wenn eine Aufgabe versucht, aus einer leeren Warteschlange zu lesen, erhält sie eine Fehlerantwort. In vielen RTOS kann ein Blockierungsmechanismus verwendet werden, um aus einer leeren Warteschlange zu lesen. Wenn die Warteschlange leer ist, kann die Aufgabe angehalten werden, bis die Nachricht von einer anderen Aufgabe an die Warteschlange gesendet wird.

Höchstwahrscheinlich gibt es im RTOS einen Mechanismus zum Senden einer Nachricht an die Vorderseite der Warteschlange, der als Jamming bezeichnet wird. Einige RTOS unterstützen auch die Broadcast-Funktion. Auf diese Weise können Sie Nachrichten an alle Aufgaben senden, die beim Lesen der Warteschlange angehalten wurden.

Darüber hinaus kann das RTOS das Senden und Lesen von Nachrichten variabler Länge unterstützen. Dies bietet mehr Flexibilität, bringt jedoch zusätzlichen Aufwand mit sich.

Viele RTOSs unterstützen eine andere Art von Kernelobjekt, die "Pipes". Im Wesentlichen ähnelt ein Kanal einer Warteschlange, verarbeitet jedoch byteorientierte Daten.

Die Funktionalität der Warteschlangen ist nicht von Interesse, aber es versteht sich, dass sie mehr Speicher- und Laufzeitaufwand haben als Postfächer, vor allem, weil zwei Zeiger gespeichert werden müssen: der Anfang und das Ende der Warteschlange.

Mutexe


Mutexe (sich gegenseitig ausschließende Semaphoren) sind unabhängige Kernelobjekte, die sich sehr ähnlich wie normale binäre Semaphoren verhalten. Sie sind etwas komplizierter als Semaphore und beinhalten das Konzept des temporären Eigentums (eine Ressource, für die der Zugriff kontrolliert wird). Wenn eine Aufgabe einen Mutex zuweist, kann nur dieselbe Aufgabe ihn wieder freigeben: Der Mutex (und damit die Ressource) gehört vorübergehend zur Aufgabe.

Mutexe werden nicht von allen RTOS bereitgestellt, aber das reguläre binäre Semaphor ist ziemlich einfach anzupassen. Sie müssen eine Mutex-Abruffunktion schreiben, die ein Semaphor und eine Aufgabenkennung zuweist. Die Zusatzfunktion „Mutex Release“ überprüft dann die Kennung der aufrufenden Task und gibt das Semaphor nur frei, wenn es mit dem gespeicherten Wert übereinstimmt. Andernfalls wird ein Fehler zurückgegeben.

Als wir an unserem eigenen Echtzeit-Betriebssystem OSRV MAX arbeiteten (zuvor veröffentlichte Artikel darüber), stieß unser Team auf den Blog von Colin Walls, einem Experten für Mikroelektronik und Firmware bei Mentor Graphics. Artikel schienen interessant, übersetzten sie für sich selbst, aber um nicht "an den Tisch zu schreiben", beschlossen sie, sie zu veröffentlichen. Ich hoffe, sie werden Ihnen auch nützlich sein. Wenn ja, planen wir, alle übersetzten Artikel in der Reihe zu veröffentlichen.

Über den Autor: Colin Walls ist seit über dreißig Jahren in der Elektronikindustrie tätig und widmet sich die meiste Zeit der Firmware. Heute ist er Firmware-Ingenieur bei Mentor Embedded (einer Abteilung von Mentor Graphics). Colin Walls spricht häufig auf Konferenzen und Seminaren, Autor zahlreicher technischer Artikel und zweier Bücher über Firmware. Lebt in Großbritannien. Colins professionelles Blog: blogs.mentor.com/colinwalls , E-Mail: colin_walls@mentor.com

Lesen Sie den ersten, zweiten, dritten und vierten Artikel, der zuvor veröffentlicht wurde.

Source: https://habr.com/ru/post/de415429/


All Articles