Wie Echtzeit-Software auch ohne Echtzeit-Betriebssystem entwickelt werden kann

Autor / Redakteur: David Kalinsky* / Sebastian Gerstl

Ist ein Echtzeit-Betriebssystem notwendig, wenn Echtzeit-Software entwickelt wird? David Kalinsky beleuchtet in unserem Beitrag grundlegende Designthemen.

Firmen zum Thema

Echtzeit-Software muss nicht immer auf Echtzeit-Betriebssystem-Plattformen erstellt werden
Echtzeit-Software muss nicht immer auf Echtzeit-Betriebssystem-Plattformen erstellt werden
( Gerd Altmann, pixelio.de)

Im folgenden Artikel werden grundlegende Designthemen der Embedded- und Echtzeit-Software beleuchtet. Dazu gehören das Task-Scheduling, die Kommunikation zwischen Tasks oder zwischen Interrupt Service Routinen und Tasks sowie die Unterschiede zwischen Do-it-Yourself-Lösungen und einem RTOS.

Als erstes soll der Task-Scheduler als Kernstück eines Echtzeit-Softwaresystems vorgestellt werden. Die Auswahl ist groß und reicht vom einfachen zyklischen Executive, der sich leicht selbst entwickeln lässt, bis hin zum prioritätsbasierten preemptiven Scheduler in handelsüblichen RTOS-Systemen.

Bildergalerie
Bildergalerie mit 16 Bildern

Je nach Applikation und I/O-Anforderungen kann ein Scheduler gewählt werden, der in Eigenregie entwickelt wird. Die oberen sechs Task-Scheduler-Typen lassen sich in Eigenregie entwickeln; Einzelheiten hierzu werden später erklärt. Die restlichen drei stehen in vielen handelsüblichen RTOS-Systemen zur Verfügung, sollten jedoch nur in Projekten eingesetzt werden, bei denen die Einschränkungen, welche die Typen 1-6 mit sich bringen, nicht tolerierbar sind.

In weniger komplexen Embedded-Systemen lässt sich Applikationssoftware einfach als Endlosschleife programmieren. Die in der Schleife programmierten Aktivitäten werden sequentiell ausgeführt. Verzweigungen und geschachtelte Schleifen sind möglich, vorausgesetzt, der Code wird nach der Ausführung in der Schleife wieder zum Anfang zurückgeführt.

Echtzeit-Software ohne RTOS: Eine Frage der Komplexität

In einfachen Embedded-Systemen funktioniert diese Art der Programmierung gut. Vor allem ist sie dann geeignet, wenn die Software die Codesequenz schnell ausführt und rasch zurückführen kann. Dieser Programmierstil beeinträchtigt jedoch oft die Performance. Interrupts von Hardware-Geräten lassen sich damit nicht bearbeiten, und für die Interaktion mit Software-in-the-Loop müssen die Geräte abgefragt werden.

In komplexeren Systemen lässt sich der Endlosschleifen-Ansatz weiter ausbauen. In diesen Systemen gibt es Hunderttausende von Codezeilen, die der Entwickler in separate Einheiten, den Tasks, unterteilt. Die Tasks, oder auch Prozesse genannt, sollten möglichst unabhängig voneinander sein und nur minimal interagieren. In Softwaredesigns mit einfachem zyklischen Executive werden die Tasks innerhalb einer Endlosschleife in Standardsequenzen ausgeführt. Im Großen und Ganzen entspricht dies dem Ansatz mit Endlosschleife, jedoch sind die ausgeführten Tasks hier umfangreicher. Diese Methode ist als Round-Robin-Scheduling bekannt.

Die Tasks können Informationen untereinander weitergeben, indem sie gemeinsam genutzte Daten schreiben und lesen. Eine Task wird grundsätzlich vollständig ausgeführt, ehe die nächste Task beginnt. Damit ist gewährleistet, dass eine Task keinen unvollständigen Daten weitergibt. Auch mit dieser Methode lassen sich Interrupts von Hardware-Geräten nicht bearbeiten, und für die Interaktion mit Software-in-the-Loop müssen die Geräte abgefragt werden. Diese Methode könnte man auch als „Echtzeit“-Task-Scheduling bezeichnen, vorausgesetzt, die gesamte Software wird schnell in der Schleife ausgeführt und die Schleife kann immer wieder sehr schnell laufen.

Präzises Timing mit einem zeitgesteuerten zyklischen Executive

Manchen Applikationen ist die Vorstellung, die ein einfacher zyklischer Executive von „Echtzeit“ hat, nicht präzise genug. Ein einfacher zyklischer Executive will seine Tasks so schnell und so oft wie möglich ausführen. In komplexeren Applikationen ist ein präzises Timing aber oft wichtiger als Geschwindigkeit. Mit einem zeitgesteuerten zyklischen Executive lässt sich diese Anforderung besser erfüllen. Über einen Hardwaretimer-Interrupt wird die Ausführung aller Tasks angestoßen. Alle Tasks laufen nacheinander und vollständig ab. Damit ein zeitgesteuerter zyklischer Executive richtig funktioniert, muss die letzte Task der Task-Kette vollständig ausgeführt werden, ehe der nächste Timer-Interrupt erfolgt. Die Tasks müssen auf Basis der Wiederholrate der Hardwaretimer-Interrupts ausgeführt werden.

Trotz der Hardwaretimer-Interrupts können die Tasks einfach Daten untereinander weitergeben, indem sie gemeinsam genutzte Daten schreiben und lesen. Eine Task wird immer vollständig ausgeführt, ehe die nächste Task beginnt. Interrupts von Hardware-Geräten, mit Ausnahme des Timers, lassen sich mit diesem Programmierstil nicht bearbeiten, und für die Interaktion mit den Tasks müssen die Geräte abgefragt werden.

Unterschiedliche Wiederholrate mit dem zyklischen Multirate-Executive

Der zeitgesteuerte zyklische Executive geht davon aus, dass alle Tasks mit identischer Wiederholrate laufen. Manchmal ist es jedoch erforderlich, dass Tasks mit unterschiedlicher Wiederholrate ausgeführt werden. Mit einem modifizierten, zeitgesteuerten zyklischen Executive, dem zyklischen Multirate-Executive, lässt sich dies gut umsetzen, wenn die höhere Wiederholrate ein ganzzahliges Vielfaches der „Basisrate“ ist.

In einem zyklischen Multirate-Executive werden Basisraten-Tasks einmal je Timer-Interrupt und Tasks mit höherer Wiederholrate mehrmals je Timer-Interrupt ausgeführt. Diese Anzahl ist ein ganzzahliges Vielfaches der Basisrate. Die Ausführungen der Tasks mit höherer Wiederholrate sollten möglichst gleichmäßig über die Tasksequenz verteilt werden, die auf einen Timer-Interrupt folgt. Die Basisrate wird oft als „Major Cycle“ (Hauptzyklus) bezeichnet; höhere Wiederholraten deuten auf einen so genannten „Minor Cycle“ (Nebenzyklus) hin. Jedoch hat dieses Verfahren auch Einschränkungen:

Mithilfe eines Hardwaretimer-Interrupts lassen sich Tasks mit gleichmäßigen oder unterschiedlichen Wiederholraten ausführen. Tasks können über gemeinsam genutzte Daten miteinander kommunizieren, ohne dass die Datenintegrität beeinträchtigt wird. Hardware-Geräte mit Ausnahme des Timers werden abgefragt und sind nicht interruptgesteuert.

Häufiges Abfragen der Hardware-Geräte führt zu erheblichen Einschränkungen. Wird das Gerät nicht oft genug abgefragt, können wichtige Vorgänge übersehen werden. Wird das Gerät jedoch zu oft abgefragt, beansprucht der Abfragevorgang übermäßig viel Rechenleistung.

Bildergalerie
Bildergalerie mit 16 Bildern

Timing der Tasks lässt sich nur schätzen

Daher eignen sich interruptgesteuerte Peripheriegeräte normalerweise eher für I/O. Es spricht auch gegen zyklische Executives, dass sich das Timing einer Task-Ausführung nicht präzise steuern lässt. Selbst wenn die Ausführung einer Task-Kette über Hardware-Timing ausgelöst wird, lässt sich nur das Timing für die erste Task in einer Kette präzise definieren. Die zweite Task wird erst dann ausgeführt, wenn die erste beendet wurde. Enthalten diese Tasks Code mit variabler Prozessorlast, wie datenabhängige Schleifen, hängt das Timing aller nachfolgenden Tasks in einer Kette von den Inhalten der vorangegangenen Tasks ab. Das Timing von Tasks in einer Task-Kette lässt sich also nicht präzise steuern.

Selbst wenn keine Tasks Code mit variabler Prozessorlast enthalten, lässt sich das Timing der einzelnen Tasks nur schätzen. Die beispielhafte Abbildung eines zyklischen Multirate-Executives veranschaulicht dies. In diesem Diagramm soll die als Stern dargestellte Task mit einer Wiederholrate von 40 Hz ausgeführt werden. Zwischen den sukzessiven Ausführungen dieser Task müssen also genau 25 ms oder 25.000.00 µs liegen. Betrachtet man das Diagramm als Kreis, dessen Gesamtumfang eine Basisperiode mit 10 Hz darstellt, müsste die „Stern-Task“ genau bei einem Winkel von 0°, 90°, 180° und 170° laufen, doch dem ist nicht so. Manchmal läuft sie etwas eher, dann wieder später – je nachdem, wann die vorherige Task beendet wird und wie viel Zeit die nachfolgende Task beansprucht. Wie bereits erwähnt, muss jede Task immer vollständig ablaufen und darf dabei nicht unterbrochen werden.

Ein Lösungsansatz für dieses Timingproblem ist das Zählen der Maschinenzyklen der Rechneranweisungen, die von jeder Task auszuführen sind. Damit soll präzise ermittelt werden, welcher Anteil einer Task ausgeführt werden kann, ehe eine andere Task mit genau definiertem Timing ausgeführt werden kann. Auf diese Weise ließe sich der erste Teil einer Task ausführen, dann würde die Task mit dem definierten Timing eingefügt, und anschließend der Rest der zurückgestellten Task ablaufen.

Doch diese Methode verursacht Probleme:

  • Wenn die an einem solchen Task-Switch beteiligten Tasks gemeinsame Datenstrukturen nutzen, könnte eine Umschaltung mitten im Ablauf der Task (Mid-Task-Switch) zu Dateninkonsistenz und Zahlenfehlern bei der Datenausgabe einer Task führen.
  • Nach einer Software-Wartung und den damit verbundenen Codeänderungen oder -ergänzungen bei Tasks, die vor dem Mid-Task-Switch ausgeführt werden, müssen die Maschinenzyklen erneut gezählt und das Task-Timing neu errechnet werden. Bei dieser neuen Codesituation muss eine Task dann vielleicht an einer anderen Stelle für den Mid-Task-Switch geteilt werden.

Zyklischer Multirate-Executive für periodische Tasks

Wenn alle Tasks periodisch sind, aber unterschiedliche Wiederholraten haben, eignen sich Multirate-Executives oft besser als zyklische Executives. Bei einem solchen Scheduler muss die Wiederholrate der Timer-Interrupts dem kleinsten gemeinsamen Vielfachen aller Raten der Task entsprechen. Tasks können bei jedem Timer-Interrupt (Tick) ausgeführt werden. Muss beispielsweise eine Task mit 50, 60 und 100 Hz ausgeführt werden, sind die Timer-Interrupts mit 300 Hz bereitzustellen. Die 100-Hz-Task wird bei jedem dritten Tick ausgeführt, die 60-Hz-Task bei jedem fünften und die 50-Hz-Task bei jedem sechsten.

Wenn es nicht erforderlich ist, das Timing der Tasks zu synchronisieren, können sie auch bei versetzten Ticks ausgeführt werden. Nicht alle drei Tasks müssen bei Tick 0 laufen. Die 100-Hz-Task läuft erstmals bei Tick 0, die 60-Hz-Task bei Tick 1 und die 50-Hz-Task bei Tick 2.

Jede Task muss vollständig ausgeführt werden, ehe die nächste Task startet. Wie bei den zyklischen Executives können Tasks Informationen einfach durch Schreiben und Lesen gemeinsam genutzter Daten untereinander weitergeben. Alle Hardware-Geräte ohne Timer müssen abgefragt werden.

Vorsicht beim Hinzufügen von Interrupts

Das Abfragen von Hardware-Geräten stellt eine erhebliche Einschränkung dar. Die meisten I/O-Geräte sind heute interruptgesteuert. Doch auch interruptgesteuerte Geräte können Probleme verursachen, wenn die Software mit diesen Geräten nicht richtig zusammenarbeitet. Wenn eine Interrupt Service Routine Daten genau an die Task weiterleiten will, die sie gerade unterbricht, kann die Task die erhaltenen Daten oft nicht korrekt verarbeiten. Zum Beispiel kann es vorkommen, dass die Task mit der Verarbeitung der Daten beginnt und die ISR diese Daten dann aktualisiert. Die Task muss dann die Daten zum Weiterverarbeiten erneut lesen. Ein Teil der Daten wird also basierend auf alten Datenwerten verarbeitet und der Rest mit neuen Werten. Dies kann zu einer inkonsistenten Datenausgabe der jeweiligen Task führen.

Das zeitabhängige Verarbeiten inkonsistenter Daten bezeichnet man als “Race Condition” (Wettlaufsituation) – ein schwerwiegender Fehler bei Embedded-Software, denn er tritt scheinbar nur zufällig auf und ist zudem sehr kompliziert zu beheben. Weiteres Beispiel: Eine Task und ISR kommunizieren über eine gemeinsam genutzte Datentabelle. Wenn ein Interrupt auftritt und bearbeitet wird, während die Task gerade die Tabelle liest, erhält die Task möglicherweise alte und neue Daten aus verschiedenen Tabellenbereichen. Die alten und neuen Daten sind unter Umständen inkonsistent. Fehlerhafte Ergebnisse sind die Folge.

Bildergalerie
Bildergalerie mit 16 Bildern

Die Multirate-Executive mit Interrupts

Fallen bei der Interaktion zwischen ISRs und Tasks lassen sich umgehen, indem ISRs ihre Inputdaten in einen bestimmten Buffer schreiben und die Tasks Daten aus einem separaten Buffer holen. Bei jedem Tick (z.B. des Multirate-Executives für periodische Tasks) werden Interrupts deaktiviert, und Inputdaten werden aus dem ISR-Buffer in den Task-Buffer geschrieben. Dann werden die Interrupts wieder aktiviert, und die für diesen Tick vorgesehenen Tasks können ausgeführt werden.

So können ISRs Informationen ohne die Gefahr von Dateninkonsistenz an Tasks übermitteln. Die Interrupts werden wieder aktiviert, wenn die aktuellen Tasks der Applikation laufen. Diese Methode funktioniert, wenn die Ausführung aller geplanten Tasks vor dem nächsten Tick beendet wird. Dieser Scheduler-Typ ist komplex und sollte nicht einfach „nebenbei“ in Eigenregie entwickelt werden. Bei diesem Scheduler wird jede Task vollständig ausgeführt, ehe die nächste startet. Wie bei den vorherigen Executive-Typen können Tasks Informationen durch Schreiben und Lesen gemeinsam genutzter Daten untereinander weiterleiten.

Die Einschränkung, dass Hardware-Geräte abgefragt werden müssen, besteht hier nicht; auch Interruptsteuerung ist möglich. Dennoch ist zu gewährleisten, dass die Daten eines Interrupts, die eine ISR an die Software überträgt, für die weitere Verarbeitung nicht direkt an eine Task weitergeleitet werden. Erst nach dem nächsten Timer-Interrupt werden die ISR-Daten an die Task-Buffer übertragen, was bei manchen Anwendungen unerwünschte Verzögerungen oder Komplikationen verursacht.

Kommunikation zwischen Interrupt Service Routines und Tasks

Die meisten Peripheriegeräte in Embedded-Systemen sind interruptgesteuert. In vielen Embedded- und Echtzeit-Softwaresystemen müssen Daten von interruptgesteuerten Hardware-Geräten schnell über eine ISR an die Task-Software übertragen werden - ohne die Zeitverzögerungen, die in der oben beschriebenen Methode auftreten. Dafür stehen fünf Methoden zur Verfügung, mit denen sich zudem Datenschäden aufgrund von Race Conditions vermeiden lassen:

Hier teilen sich ISR und Task einen Datenbereich, wie in der folgenden Abbildung dargestellt. Für den Zugriff auf diesen Datenbereich muss die Task zuerst den Interrupt deaktivieren und danach wieder aktivieren. So wird sichergestellt, dass die ISR nicht eingreift, solange die Task auf die gemeinsamen Daten zugreift, und Datenschäden aufgrund von Race Conditions werden vermieden.

Falls die Hardware trotz der Deaktivierung versucht, Interrupts zu senden, können Interrupts und die zugehörigen Daten verloren gehen. Dies lässt sich mithilfe einer Hardware-Interrupt-Latch oder Interrupt-Queue vermeiden, die viele Prozessoren für Embedded-Anwendungen zur Verfügung stellen.

Gemeinsam von ISR und Task genutzter Datenbereich mit Update-Zählung

Wenn Interrupts nicht deaktiviert werden sollen, kann stattdessen eine Update-Zählfunktion (Update-Count) verwendet werden. Dies ist eine Variable innerhalb der gemeinsam genutzten Daten. Sobald die ISR Daten in den gemeinsam genutzten Bereich schreibt, erhöht sich dabei auch der Update-Count. Beim Lesen von Daten im gemeinsam genutzten Bereich liest die Task den Update-Count vor und nach dem Lesevorgang. Stimmen die beiden Update-Counts überein, heißt das, dass in der Zwischenzeit keine ISR ausgeführt wurde und somit die von der Task gelesenen Daten in Ordnung sind. Bei unterschiedlichen Update-Count-Werten muss die Task erneut ausgeführt werden und neue Daten aus dem gemeinsam genutzten Bereich lesen (und dabei wieder den Update-Count prüfen).

In dieser Methode werden keine Interrupts deaktiviert, Daten können dennoch verloren gehen, wenn mehrere Interrupts von Eingabegeräten eintreffen, während die Task versucht, im gemeinsam genutzten Datenbereich zu lesen.

Gemeinsam von ISR und Task genutzter Datenbereich mit Update-Bit

Die beschriebene Methode lässt sich verbessern, wenn der Update-Count durch ein Update-Bit ersetzt wird. Auf null zurückzusetzen ist umständlich. Wenn die ISR Daten in den gemeinsam genutzten Bereich schreibt, wird das Update-Bit auf 1 gesetzt. Zum Lesen in einem gemeinsamen Bereich muss eine Task vorher das Update-Bit auf 0 setzen. Nach Beenden des Lesevorgangs überprüft die Task, ob das Update-Bit noch auf 0 ist. In diesem Fall wurde zwischenzeitlich keine ISR ausgeführt und die gelesenen Daten sind in Ordnung. Enthält das Update-Bit eine 1, muss die Task erneut starten und neue Daten aus dem gemeinsam genutzten Datenbereich lesen (und dabei wieder das Update-Bit prüfen). Auch hier können Daten verloren gehen, z.B. wenn mehrere Interrupts von Eingabegeräten eintreffen, während die Task versucht, im gemeinsam genutzten Datenbereich zu lesen.

ISR-Task-Kommunikation: Weitere Methoden und Fragen

Es gibt noch mehr Methoden, um Daten über eine ISR schnell von interruptgesteuerten Hardware-Geräten an die Task-Software zu übertragen und dabei Zeitverzögerungen und Datenschäden aufgrund von Race Conditions zu vermeiden. Wenn ein Mikroprozessor in seiner Maschinensprache über einen atomaren „Test- und Set-Befehl“ verfügt, lässt sich damit ein binärer Semaphor wie in einem RTOS entwickeln.

Möglich ist auch ein Ringspeicher-System, bei dem die ISR eines Eingabegeräts in den einen Buffer schreibt, während eine Task Daten in einem anderen Buffer liest. Der Trick liegt darin, die Zeiger mit atomaren (unterbrechungsfreien) Operationen zu bewegen und die Lese- und Schreibzeiger zu voneinander getrennt zu halten (bzw. Abweichungen entsprechend zu erkennen).

Bildergalerie
Bildergalerie mit 16 Bildern

Die Grenze zwischen selbst entwickelten Lösungen und professionellen RTOS-Systemen

Die zwei zuletzt beschriebenen Methoden sind ähnlich komplex wie die Eigenentwicklung eines RTOS. Ehe Sie nun aber in den sauren Apfel beißen und bei verschiedenen Herstellern Informationen über deren RTOS einholen, stellen Sie sich erst diese Frage: „Wenn sich also ISR-Gerätedaten schnell in Datentabellen übertragen lassen, ist es dann immer noch sinnvoll, Tasks weiterhin in einer normalen, vorab definierten Sequenz auszuführen? Ist es wirklich erforderlich, dass eine bestimmte Task „aus der Reihe tanzt“ und jetzt sofort ausgeführt werden muss?“

Wenn die von der ISR gelieferten Daten wirklich auf der Stelle verarbeiten werden müssen, ist ein preemptiver Task-Scheduler vonnöten, den viele handelsübliche RTOS zur Verfügung stellen.

Kürzere Reaktionszeiten mit preemptivem Task-Scheduling

Die eingangs beleuchteten Task-Scheduler werden als „nicht-preemptiv“ bezeichnet, da ein Umschalten zwischen Tasks erst dann erfolgt, wenn eine Task vollständig ausgeführt wurde und die nächste Task ausgeführt werden soll (von Anfang an). In vielen Fällen lässt sich die Reaktionszeit mithilfe eines preemptiven Schedulers verkürzen, der jederzeit während der Ausführung einer Task ein Task-Switching ermöglicht (auch wenn die Ausführung der Task noch nicht abgeschlossen ist).

Bei einem Interrupt könnte die ISR zum Beispiel etwa so reagieren: „Mir ist egal, welche Task vor meinem Interrupt ausgeführt wurde. Den nächsten Timer-Tick will ich nicht abwarten. Ich will, das Task 67 jetzt sofort ausgeführt wird!“. Mit einem preemptiven Scheduler ist dies möglich.

Ein preemptiver Scheduler ist jedoch viel komplexer als ein nicht-preemptiver Scheduler. Zudem beansprucht ein solcher Scheduler sehr viel RAM-Kapazität für das Speichern von Task-Kontext und anderen Task-Statusinformationen. Es wäre sehr aufwändig, einen solchen Scheduler zu entwickeln, und die meisten standard RTOS-Systeme verfügen über preemptive Scheduler.

Mit einem preemptiven Scheduler lassen sich Hardware-Geräte abfragen oder über Interrupt steuern. Die von einem Interrupt gelieferten und an die Software übertragenen Informationen können zur weiteren Verarbeitung direkt an eine Task weitergeleitet werden, um eine kurze Reaktionszeit zu gewährleisten.

Vorsicht! Auch preemptive Scheduler verursachen manchmal Probleme

Preemptive Scheduler bieten mehr Vorteile und Funktionalität als einfachere, in Eigenregie entwickelte Scheduler. Doch ihre Komplexität birgt auch Probleme, deren sich der Softwareentwickler bewusst sein sollte. Das erste Problem: Welche Tasks dürfen eine bestimmte Task verdrängen, während diese ausgeführt wird? Die Lösung liegt darin, jeder Task eine Prioritätszahl zuzuordnen. Tasks mit höherer Priorität können Tasks mit niedrigerer Priorität verdrängen – nicht jedoch anders herum. Ein preemptiver Scheduler muss wissen, welche Priorität die nächste Task hat, die er schedulen kann.

Ein weiteres Problem ist, dass Tasks, die verdrängt werden und andere Tasks verdrängen können, Informationen nicht durch das Schreiben und Lesen gemeinsam genutzter Daten untereinander weiterleiten. Die einfachen Methoden der Datenweitergabe zwischen Tasks, die bei nicht-preemptiven Schedulern funktionieren, sind bei preemptiven Schedulern nicht möglich.

Bei einem preemptiven Scheduler gibt es ein Problem bei der Datenweitergabe zwischen Tasks durch Schreiben und Lesen gemeinsam genutzter Daten: Wenn eine Task eine andere Task verdrängt, wenn diese gerade eine gemeinsame Datentabelle liest, so liest diese andere Task in den verschiedenen Tabellenbereichen vielleicht alte und neue Daten, nachdem die erste (verdrängende) Task neue Daten in die Tabelle geschrieben hat. Diese Kombination alter und neuer Daten führt zu Dateninkonsistenz und teilweise zu fehlerhaften Ergebnissen

Dieses Problem ist im Wesentlichen dasselbe, das auftritt, wenn eine ISR und eine Task durch Schreiben und Lesen gemeinsam genutzter Daten miteinander kommunizieren wollen, wie oben beschrieben.

Ein Betriebssystem sollte über einen preemptiven Scheduler verfügen und Mechanismen zur Datenweitergabe zwischen den Tasks und an eine ISR /von einer ISR bereitstellen. Dafür bieten RTOS unterschiedliche Methoden an. Message-Queues verwendet eine Zählsemaphore, Event-Flags und asynchrone Signale. Bei Automobilanwendungen, die den OSEK-Standard erfüllen, gibt es „Ressourcen“, „Events“, „Alarm“ und „Nachrichten“. Betriebssysteme, die besonders für hohe Verfügbarkeit in verteilten Multiprozessor-Umgebungen ausgelegt sind, setzen bei der Task-Kommunikation wiederum auf Messages.

Wenn Informationen von einer Task zur anderen weitergegeben werden sollen, müssen diese Mechanismen eingesetzt werden. So ist eine sichere Datenweitergabe in einer preemptiven Umgebung gewährleistet.

* *David Kalinsky ist Leiter für Kundentraining bei D. Kalinsky Associates – Technical Training, einem Anbieter von Intensivseminaren für professionelle Embedded System- und Softwareentwickler.

Artikelfiles und Artikellinks

(ID:24232920)