Die ganze Wahrheit über RTOS von Colin Walls. Artikel 4. Aufgaben, Kontextwechsel und Interrupts

Aufgabenkennungen


Sie müssen in der Lage sein, jede Aufgabe im System zu identifizieren. Diese Anforderung ist für andere Kernelobjekte wichtig, es gibt jedoch einige Nuancen in den Aufgaben, die dem Thema dieses Artikels entsprechen.



RTOS-Entwickler verwenden unterschiedliche Ansätze zur Identifizierung von Aufgaben, es können jedoch vier allgemeine Strategien unterschieden werden:

  • Die Aufgabe wird mit einem Zeiger auf ihren „Steuerblock“ identifiziert. Zeiger sind immer eindeutig und auch bequem zu verwenden, da für viele API-Aufrufe der Zugriff auf die Steuereinheit erforderlich ist. Dies bedeutet, dass alle Aufgabendaten im Arbeitsspeicher (RAM) gespeichert sind, was ineffizient sein kann. Ein Zeiger belegt normalerweise ungefähr 32 Bit Speicher.
  • Eine Aufgabe kann mit einer beliebigen "Indexnummer" definiert werden. Dieser Wert kann nützlich sein, wenn Sie Zugriff auf Datensätze in bestimmten Tabellen gewähren. Eine solche Kennung kann abhängig von den Einschränkungen der Anzahl der vom RTOS unterstützten Aufgaben acht oder weniger Speicherbits belegen.
  • Einige RTOSs erlauben nur eine Aufgabe pro Prioritätsstufe und verwenden daher die Priorität, um eine Aufgabe eindeutig zu identifizieren. Dies bedeutet, dass die Priorität der Aufgabe nicht geändert werden kann. Dieser Ansatz ist eine Variation des vorherigen Ansatzes.
  • Aufgaben können Namen haben, die Zeichenfolgen sind. Dies kann für das Debuggen nützlich sein, ist jedoch wahrscheinlich kein wirksames Mittel zur eindeutigen Identifizierung einer Aufgabe. RTOSs, die die Benennung von Aufgaben unterstützen, verfügen normalerweise über eine zusätzliche Kennung (z. B. einen Zeiger), die von API-Aufrufen usw. verwendet wird. Bei den meisten eingebetteten Systemen sind Textnamen über Kopf. Mit einem guten Debugger können Sie sie lokal auf dem Host aufrufen.

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

Kontextwechsel


Beim Kontextwechsel wird die Kontrolle von einer Aufgabe auf eine andere übertragen. Dieses Thema sollte näher untersucht werden, da die Funktionsweise der Kontextumschaltung ein grundlegendes Prinzip des RTOS ist.

Was ist eine Aufgabe?


Wir wissen, dass eine Aufgabe ein quasi unabhängiges Programm ist, das die Prozessorzeit mit einer Reihe anderer Aufgaben unter der Kontrolle von RTOS teilt. Aber Sie müssen darüber nachdenken, was die Aufgabe wirklich auszeichnet.

Registersatz


Eine Aufgabe ist letztendlich ein eindeutiger Satz von Prozessorregisterwerten. Sie werden entweder in Prozessorregister geladen (dh die Aufgabe ist aktuell) oder irgendwo bis zur geplanten Ausführungszeit gespeichert. In einer idealen Welt hätte ein Zentralprozessor mehrere Registersätze, und jeder könnte einer separaten Aufgabe zugeordnet werden. Dies wurde für besondere Anlässe umgesetzt. Vor vielen Jahren hatte die Texas Instruments TI 9900-Serie viele Registersätze für jeden Job, aber sie wurden im Hauptspeicher implementiert, was die Leistung einschränkte. Die SPARC-Architektur (zuvor auf Unix-Desktop-Systemen verwendet) unterstützt viele Registersätze in der „Ringstruktur“, die Anzahl der Sätze ist jedoch immer noch begrenzt.

Interne Daten


Die Aufgabe wird wahrscheinlich einen eigenen Stapel haben, dessen Größe für jede Aufgabe separat festgelegt werden kann oder ein globaler Parameter für alle Aufgaben im System sein kann. Dies bietet zusammen mit den Registern eine Datenspeicherung für bestimmte Aufgaben. Möglicherweise gibt es andere Speicherbereiche zum Speichern von Daten für eine bestimmte Aufgabe.

Geteilte Ressourcen


Nahezu alle Ressourcen können von Aufgaben gemeinsam genutzt werden. Der Code kann allgemein sein: entweder bestimmte Funktionen oder der gesamte Aufgabencode. Es muss sichergestellt werden, dass der Code wiedereintrittsfähig ist. Zunächst sollten keine statischen Variablen verwendet werden (angegeben als statisch oder direkt außerhalb der Funktion). Seien Sie vorsichtig mit Standardbibliotheksmodulen, die nicht für den integrierten Gebrauch vorgesehen sind. Sie haben normalerweise viele unzuverlässige Funktionen.

Datenaustausch ist ebenfalls möglich, eine sorgfältige Zugriffskontrolle ist jedoch erforderlich. Im Idealfall ist zu einem bestimmten Zeitpunkt nur eine Aufgabe der "Eigentümer" der Daten.

Wie man den Kontext behält


Wenn eine Aufgabe neu geplant wird (dh nicht mehr aktuell ist), muss ihr Registersatz irgendwo gespeichert werden. Es gibt mindestens zwei Möglichkeiten:

  • Register können für Aufgaben in einer speziellen Tabelle gespeichert werden. Kann Teil eines Task Control Blocks (TCB) sein. Die Größe ist ein vorhersehbarer und konstanter Wert (für eine bestimmte CPU-Architektur).
  • Register können auf den Taskstapel verschoben werden. Dies erfordert die Zuweisung von ausreichend zusätzlichem Stapelspeicher und die Speicherung des Zeigers (möglicherweise im TCB).

Die Wahl des Mechanismus hängt von den Merkmalen eines bestimmten RTOS und vom Zielprozessor ab. Einige (normalerweise 32-Bit-) Geräte können effizient auf den Stapel zugreifen. Der Rest (z. B. 8-Bit) ist möglicherweise optimaler, wenn Sie mit Tabellen arbeiten.

Dynamische Aufgabenerstellung


Der Hauptaspekt der RTOS-Architektur ist, dass das RTOS entweder "statisch" oder "dynamisch" ist.

Bei Verwendung eines statischen RTOS wird während der Erstellung der Anwendung alles bestimmt, insbesondere die Anzahl der Aufgaben im System. Dies ist eine logische Lösung für eingebettete Anwendungen, die normalerweise nur über eingeschränkte Funktionen verfügen.

Dynamic RTOS startet eine Aufgabe (die eine spezielle Hauptaufgabe sein kann) und erstellt und löscht bei Bedarf auch andere Aufgaben. Dies ermöglicht es dem System, sich an sich ändernde Anforderungen anzupassen, und ist ein näheres Analogon zum Desktop-System, das sich auf diese Weise verhält. Die statische / dynamische Ansicht gilt auch für andere Kernelobjekte.

Anforderung für die dynamische Aufgabenerstellung


Diese Funktion ist in den meisten kommerziellen RTOS enthalten. Allerdings benötigt nur ein kleiner Teil der Anwendungen wirklich einen dynamischen Betriebsmodus. Sehr oft startet das System, erstellt alle erforderlichen Aufgaben (und andere Objekte) und erstellt und zerstört dann nie mehr den Anwendungscode. Die Fähigkeit, dynamische Aufgaben zu erstellen, ist zu einer Formalität geworden. Ein Lieferant stellte es vor, alle anderen folgten dem Beispiel.

Es ist bemerkenswert, dass der OSEK / VDX-Standard eine statische Architektur erfordert, obwohl dies für ziemlich komplexe Anwendungen gelten kann. Das Ergebnis dieser Anforderungen ist die Unfähigkeit, OSEK / VDX mit einem Wrapper zu implementieren, einer Zwischenschicht auf einem regulären (dynamischen) RTOS.

Fallstricke bei der dynamischen Aufgabenerstellung


Es gibt verschiedene Probleme mit dem dynamischen Betriebsmodus, die problematisch sein können.

Erstens wird das System komplexer, was bedeutet, dass für Datenstrukturen, die Aufgaben (TCBs) beschreiben, zusätzliche Informationen benötigt werden. In der Regel werden sie in Form von bidirektionalen Listen implementiert, was zu Kosten im Zusammenhang mit der Speichermenge führt.
Alle Daten, die die Aufgabe beschreiben, müssen im RAM gespeichert werden. Dies ist ineffizient, da die meisten von ihnen einfach persistente Datenelemente sein können, die aus dem ROM kopiert wurden. Außerdem wird auf Prozessoren niedrigerer Ebene (Mikrocontroller) möglicherweise kein RAM abgerufen.

Am besorgniserregendsten ist wahrscheinlich die Möglichkeit eines unvorhersehbaren Ressourcenmangels, der dazu führen kann, dass keine neuen Objekte erstellt werden können. Da das Wesentliche eines Echtzeitsystems seine Vorhersagbarkeit ist, ist dies nicht akzeptabel. Daher muss bei der Erstellung dynamischer Aufgaben (und anderer Objekte) vorsichtig vorgegangen werden.

Unterbrechungen


Es ist möglich, dass ein eingebettetes Echtzeitsystem ohne die Verwendung von Interrupts implementiert werden kann, dies ist jedoch nicht typisch.

Interrupts und Kernel


Bei Verwendung von RTOS wird ein Interrupt-Handler (ISR) so einfach wie möglich gemacht, um die minimale Prozessorzeit für geplante Aufgaben zu „stehlen“. Oft kann ein Gerät einfach gewartet werden, und jede erforderliche Aufgabe wird zur Verarbeitung in die Warteschlange gestellt. Darüber hinaus ist es schwierig, allgemein über Interrupts und deren Interaktion mit den Kerneln zu sprechen, einfach weil sie sehr unterschiedlich sind. Einerseits kann der RTOS-Entwickler sicherstellen, dass sich Interrupts überhaupt nicht auf den Kernel beziehen, und der Programmierer muss sicherstellen, dass der Taskplaner nicht überlastet wird, was viel Prozessorzeit im ISR erfordert. Andererseits kann das RTOS das gesamte Interrupt-Subsystem vollständig steuern. Keiner der beschriebenen Ansätze ist richtig oder falsch, sie sind nur unterschiedlich.

Kontext speichern


ISRs müssen immer einen „Kontext“ beibehalten, damit unterbrechbarer Code nicht von ISR-Berechnungen beeinflusst wird. In einem System, das ohne RTOS implementiert ist, müssen lediglich alle vom ISR verwendeten Register (normalerweise auf dem Stapel) gespeichert und vor der Rückkehr wiederhergestellt werden. Einige Prozessoren verfügen über einen dedizierten ISR-Stapel, während andere einfach denselben Stapel wie der Anwendungscode verwenden.

Bei Verwendung von RTOS kann der Ansatz genau der gleiche sein. Auf die gleiche Weise kann der vom ISR verwendete Stapel von der aktuellen Aufgabe "ausgeliehen" werden, oder es kann sich um einen anderen Stapel handeln, der für Interrupts zugewiesen ist. Einige Kerne implementieren diese Funktion, auch wenn der Prozessor selbst den Interrupt-Stack nicht unterstützt. Die Situation ist kompliziert, wenn der ISR einen API-Aufruf ausführt, der sich auf den Taskplaner auswirkt. Dies kann dazu führen, dass der Interrupt zu einer anderen Aufgabe zurückkehrt, die zum Zeitpunkt des Interrupts gestartet wurde.

Interrupts und Scheduler


Es gibt verschiedene Umstände, unter denen der ISR-Ausführungscode zu einer anderen Aufgabe zurückkehren kann:

  • ISR kann einer bereits abgeschlossenen Aufgabe eine höhere Priorität zuweisen als der aktuellen, wenn der Prioritätsaufgabenplaner verwendet wird.
  • ISR kann die aktuelle Aufgabe anhalten.
  • Mit dem Time-Slice-Scheduler (TS) steuert der System-Timer-Interrupt-Handler die Zeitintervalle und kann den Scheduler bei Bedarf aufrufen.

Uhr Timer (Tick Clock)


In eingebetteten Systemen wird häufig die Verwendung eines periodischen "Taktgebers" (Zeitscheibe) gefunden. In einigen RTOS ist dies eine erforderliche Komponente. In der Regel ist das Vorhandensein eines Zeitgebers optional, und sein Fehlen schließt lediglich die Verfügbarkeit bestimmter Dienste aus. Der Timer-Interrupt-Handler bietet normalerweise vier Funktionen:

  • Wenn ein Zeitfenster-Scheduler verwendet wird, steuert der Timer-Interrupt-Handler den Zeitzähler und plant jedes Mal, wenn die Zeit abläuft, eine neue Aufgabe.
  • Bietet Systemzeitunterstützung. Dies ist hauptsächlich eine 32-Bit-Variable, die von einem Timer inkrementiert wird und von Aufgaben vordefiniert oder angefordert werden kann.
  • Wenn das RTOS Anwendungen mit Timern versorgt, wird es von einem Timer-Interrupt-Handler unterstützt, der für den Ablauf und die Neuplanung verantwortlich ist.
  • Wenn das RTOS Zeitüberschreitungen beim Blockieren von API-Aufrufen unterstützt oder sich Aufgaben möglicherweise im Ruhezustand befinden, werden diese Zeitintervalle vom Timer-Interrupt-Handler unterstützt.

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 und dritten Artikel der zuvor veröffentlichten Reihe.

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


All Articles