Durch ein Echtzeitbetriebssystem können die Vorteile von Tasks und Echtzeit-Zeitverhalten genutzt werden, an denen in manchen Anwendungen kein Weg vorbeiführt. In diesem Artikel werden drei dieser Betriebssysteme vorgestellt und in Bezug auf verschiedene Kenngrößen und Funktionalitäten verglichen.
Checkliste – Wie komme ich zum richtigen RTOS? |
---|
Einige Anhaltspunkte für die Auswahl eines RTOS:
|
Immer da, wo die Abarbeitung von Prozessen schnell getaktet und in einer bestimmten Zeit erfolgen muss, kommen sie zum Einsatz: Echtzeitbetriebssysteme. Ob in der Steuerung für Motoren oder für die Überwachung von sicherheitskritischen Prozessgrößen, Embedded Systems können mit einem Echtzeitbetriebssystem (engl. Real-Time Operating System, kurz: RTOS) ausgestattet wichtige Aufgaben übernehmen, und das unter Einhaltung bestimmter Rahmenbedingungen, die in Anbetracht von Sicherheits- und Qualitätsaspekten eine große Rolle spielen. Doch auch Anwendungen ohne Echtzeitanforderungen, deren Komplexität über den sinnvollen Einsatz eines »Super-Loops« – also die Abarbeitung des Programms in einer einzigen großen while-Schleife – hinausgeht, können von einem solchen Betriebssystem profitieren.
Wenn es darum geht, welches RTOS für eine Embedded-Applikation benutzt werden soll, steht man oft vor der Qual der Wahl: Allein Wikipedia listet über 100 Betriebssysteme, die sich das Prädikat »echtzeitfähig« auf die Fahne geschrieben haben. Um es dem Leser vielleicht ein bisschen einfacher zu machen, eine (hoffentlich richtige) Entscheidung zu treffen, werden in diesem Artikel drei bekannte Vertreter dieser Betriebssystem-Gattung verglichen: FreeRTOS, Zephyr und RIOT OS.
Alle drei RTOS sind relativ jung, wobei Zephyr mit dem Erst-Release im Jahr 2016 einer der jüngsten Neuzugänge in der RTOS-Landschaft ist und unter der Schirmherrschaft der Linux Foundation steht. Auch das ursprünglich 2003 von Richard Barry veröffentlichte FreeRTOS hat seit der Entwicklungsübernahme durch Amazon einen prominenten Koordinator gefunden. RIOT OS hingegen ging aus dem Forschungsprojekt FeuerWhere der Freien Universität Berlin hervor, bei dem vernetzte Sensoren Vitalwerte von Feuerwehrleuten im Einsatz überwachen. Es wird als Open-Source-Software von einer wachsenden Community weiterentwickelt.
Um gleich einmal ein paar Gemeinsamkeiten aufzuzählen: Die hier vorgestellten Echtzeitbetriebssysteme sind kostenlos, haben permissive Open-Source-Lizenzen (FreeRTOS: MIT, Zephyr: Apache 2.0, RIOT: LPGL), werden in C programmiert, ermöglichen Multitasking und Inter-Task-Kommunikation und sind für sehr viele Boards bzw. Mikrocontroller-Architekturen portiert. Interessant wird es bei den Unterschieden, wobei zu beachten ist, dass auch die Zielarchitektur die Nutzung bestimmter OS-Features einschränken oder erst ermöglichen kann. Übrigens benutzt von den hier vorgestellten RTOS nur FreeRTOS den Begriff »Task«, Zephyr und RIOT OS hingegen den Begriff »Thread«. Beide beschreiben eine auszuführende Einheit von Arbeit und können im Embedded-Bereich meistens synonym verwendet werden.
Sieht man sich den Speicherverbrauch an, erreichen die drei Betriebssysteme eine sehr niedrige Belegung von RAM und ROM. Die Tabelle zeigt – neben einer Reihe anderer Eigenschaften, die nachfolgend erläutert werden – die Angaben der Projekte zum Speicherverbrauch in einer Minimalkonfiguration. Dabei ist zu beachten, dass diese Verbrauchseite stark von Faktoren wie Zielarchitektur, Compiler-Toolchain und Optimierungen, ausgewählten Features des Kernels und vielen mehr abhängt, weswegen hier keine allgemeine Gültigkeit besteht. Trotzdem bekommt man ein Gefühl dafür, mit welch geringen Ressourcen diese Systeme arbeiten können. Nur zum Vergleich: Die altbekannte printf()-Funktion aus der C-Standardbibliothek belegt mit etwa 8 kB genau so viel Speicher wie Zephyr in der Minimalversion.
Wenn es um die Repository-Größe geht, sticht Zephyr heraus: Im Installationsprozess werden mehrere Gigabyte an Daten heruntergeladen, was darauf zurückzuführen ist, dass die Hardware-Abstraction-Layers (HAL) aller möglichen Hersteller in die Zephyr Toolchain integriert werden. Mit der richtigen Konfiguration kann der Download auf die benötigten HALs beschränkt werden, was die Festplatte schont. FreeRTOS und RIOT OS kommen mit ein paar Megabyte an Download schon deutlich schlanker daher.
Echtzeitbetriebssysteme können dem Programmierer teilweise viel Arbeit ersparen, wollen aber andererseits auch richtig konfiguriert und programmiert werden: Darf der Scheduler eine andere Task mit höherer Priorität unterbrechen (Preemption), sind verschachtelte Interrupts erlaubt und wie viel Platz wird einer Task auf dem Stack zur Verfügung gestellt? Was das Thema Konfiguration angeht, gehen die drei RTOS ihre eigenen Wege: Während die Konfiguration von FreeRTOS fast ausschließlich in einem Header-File namens FreeRTOSConfig.h stattfindet, benutzt Zephyr die vom Linux-Kernel bekannte Kconfig-Language. RIOT OS geht den Weg über Makefiles. Jedoch können nicht unbedingt alle Einstellungen auf diese Weise vorgenommen werden. Während zum Beispiel in FreeRTOS und Zephyr die Anpassung der möglichen Task-Prioritäts-Level über die oben genannten Konfigurationen geschieht, muss dafür bei RIOT OS ein Makro im Headerfile des Schedulers (sched.h) angepasst werden. Wenn für ein Projekt also etwas exotischere Funktionalitäten vom RTOS gefordert sind, sollte zuerst in der Dokumentation recherchiert werden, ob und in welcher Weise sie konfiguriert werden können. Zusätzlich zur Kernel-Konfiguration mittels Kconfig erlaubt Zephyr auch das Management von Peripheriegeräten über Devicetrees.
Die Entwicklung von RIOT-OS-Applikationen gestaltet sich unter Linux am einfachsten. Windows-Usern wird empfohlen, das Windows-Subsystem for Linux (WSL) zu benutzen, was im Test auch gut funktioniert hat. Dabei kann ein Board aus der WSL heraus geflasht werden, indem der USB-Port mit dem Tool usbipd mit der WSL verknüpft wird. Bei der Nutzung von FreeRTOS profitiert der im Test benutzte STM32 davon, dass das RTOS in der Entwicklungsumgebung STM32CubeIDE als Middleware-Komponente sehr bequem in die Applikation eingebunden werden kann.
Ähnlich RIOT OS ist die Ordnerstruktur eines frischen Zephyr-Projekts sehr aufgeräumt, ja fast schon leer. Während man bei FreeRTOS einige Dutzend C-Sourcen und Headerfiles im Projektbaum stehen hat, begegnen einem bei Zephyr nur eine Handvoll Konfigurationsdateien und die Datei main.c im Ordner src. Diese Aufgeräumtheit kommt zum einen von der starken Abstraktion der Hardware, zum anderen aber auch von der Trennung von projektspezifischem Code und der Zephyr-Code-Base inklusive Zephyr SDK. Wenn dann noch die VSCode-Entwicklungsumgebung gut aufgesetzt ist, indem nützliche Extensions installiert sowie Kurzbefehle zum Bauen, Flashen und Debuggen definiert wurden, geht die Entwicklung wie von selbst von der Hand. Dabei hilft vor allem Zephyrs mitgeliefertes Metatool west, das als selbst proklamiertes »Schweizer Taschenmesser« all diese Prozesse abstrahiert, aber mit Python, CMake, OpenOCD, JLink und weiteren Tools arbeitet. Durch diese Abstraktion macht Zephyr den Einstieg in die Entwicklung sehr einfach. Nicht selten ist in der Entwicklung die Architektur bzw. der Prozessor vorgegeben. Auflistungen für die Kompatibilität aller drei OS zu verschiedenen Boards finden sich auf den jeweiligen Webseiten. In der Konfiguration von west lassen sich wie oben beim Thema »Speicherverbrauch« angedeutet auch die herunterzuladenden HALs einschränken, um die Repository-Größe gering zu halten.
Doch was, wenn der benötigte Prozessor nicht mit dabei ist? Entweder man wechselt auf ein anderes OS oder man macht sich die Mühe, das Betriebssystem auf eine neue Architektur zu portieren. Der damit verbundene Aufwand hängt stark von der Implementierung des RTOS ab und davon, ob es schon Portierungen für ähnliche Prozessoren und Boards gibt, an denen man sich orientieren kann. Eine gelungene Portierung kann dem Projekt – ganz im Sinne von Open Source – auch zur Verfügung gestellt werden.
Zephyr | FreeRTOS | RIOT OS | |
---|---|---|---|
Architektur | Modularer Mikrokernel | Mikrokernel/monolithisch | Modularer Mikrokernel |
ROM-Verbrauch | 8 kB | 5 kB | 3,2 kB |
RAM-Verbrauch | 4 kB | 4 kB | 2,8 kB |
Unterstützung | Arm (Cortex-M0, -M3, -M4, -M23, -M33, -R4, -R5, -A53), x86, ARC, RISC-V, Nios II, Xtensa, SPARC |
Arm, AVR, AVR32, ColdFire, ESP32, HCS12, IA-32, Cortex-M3-M4-M7, Infineon XMC4000, MicroBlaze, MSP430, PIC, PIC32, Renesas H8/S, RISC-V, RX100-200-600-700, 8052, STM32, TriCore, EFM32 | Arm, MSP430, AVR, x86, RISC-V |
Entwicklungstools | Gute Integrierbarkeit in VSCode, Command-Line-Tools | u. a. Plugins für Eclipse und Visual Studio, in CubeIDE integriert | Command Line Tools, VSCode Plugin |
POSIX-Kompatibilität | Implementiert teilweise den POSIX-Standard. Siehe auch POSIX-Beispielprojekte | Third Party-Bibliotheken, die eingeschränkte POSIX-Funktionalität bieten | Ebenfalls partielle POSIX-Kompatibilität durch POSIX-Wrapper |
Anzahl Tasks/Threads | Konfigurierbar | Konfigurierbar | Konfigurierbar |
Anzahl Prioritäts-Level | Konfigurierbar | Default: 5, konfigurierbar | 16 oder 256 |
Lizenz | Apache 2.0 | MIT | LGPL |
Semaphoren | Flexible Semaphore und Mutex |
Zählende und binäre Semaphoren, Mutex | Semaphore und Mutex |
Message-Buffer | Ja | Ja | Ja |
Schnittstellen | Große Auswahl an Peripherals | Je nach Board-Implementierung | Große Auswahl an Peripherals |
Installation | Getting Started Guide | FreeRTOS Kernel Quick Start Guide | Getting Started |
Dokumentation | Gut, viele Tutorials auf Website | Sehr gut, viele Tutorials z. B. auf YouTube | Wachsende Community, schwer, gute Tutorials zu finden |
Community | Wachsende Community, Linux Foundation | Große Open-Source-Community | Wachsende Community |
Übersicht über die wichtigen Eigenschaften der drei RTOS anhand jeweils einer Minimalapplikation. Die hier als Übersicht zusammengefassten Angaben werden im Text genauer erläutert.
Wie der Name schon sagt, können Echtzeitbetriebssysteme bestimmte Anforderungen an die zeitliche Abarbeitung ihrer Aufgaben erfüllen. Hierbei muss »in Echtzeit« aber nicht zwingend »schnell« bedeuten: Echtzeit beschreibt nur, dass Aufgaben bzw. Tasks garantiert bis zu einer gewissen Frist ein Ergebnis liefern. Meistens wird eine Echtzeitanforderung als »hart« definiert, wenn das Verpassen dieser Frist schwerwiegende Folgen haben kann, z. B. bei einem Steuergerät in einem Flugzeug. Wenn eine Aufgabe in den meisten Fällen rechtzeitig abgearbeitet werden soll, es aber durchaus zu Überschreitungen kommen darf, spricht man von weicher Echtzeit. Wie stringent die Anforderungen an das Echtzeitverhalten eines Systems sein sollen, muss von Fall zu Fall entschieden werden. Ist für bestimmte Routinen harte Echtzeit gefordert, bieten Zephyr und FreeRTOS die Möglichkeit von Zero-Latency-Interrupts, bei denen durch Erhöhung der Interrupt-Priorität sichergestellt wird, dass der RTOS-Kernel während der ISR keine andere Funktionen ausführt. In der RIOT-OS-Dokumentation findet sich nichts über eine solche Funktionalität. Oft kommen für Anwendungen mit harten Echtzeitanforderungen proprietäre RTOS mit Sicherheitszertifizierung zum Einsatz. Ein Beispiel dafür ist das auf FreeRTOS basierende SaferTOS der Firma Wittenstein.
Um zu entscheiden, welche Task die Rechenzeit der CPU beanspruchen darf, setzen Betriebssysteme jeweils einen Scheduler ein. Dieser wählt aus, welcher Task als Nächstes an die Reihe kommt. Bei RTOS kommen heute meistens prioritätsbasierte Scheduler zum Einsatz, wobei jeder Task eine Priorität zugeteilt wird. Der Scheduler teilt dann der Task, die die höchste Priorität besitzt und gerade lauffähig ist, Rechenzeit zu. Außerdem kümmert sich der Scheduler um das Context-Switching, also darum, dass einer Task, auch nachdem sie unterbrochen und wieder fortgeführt wurde, immer die für sie richtigen Daten (z. B. in Registern) zur Verfügung stehen.
In RIOT OS, FreeRTOS und Zephyr kann einer Task zur Laufzeit eine andere Priorität zugewiesen werden. Damit lassen sich komplexere Programmabläufe realisieren, etwa wenn eine Task, die die ganze Zeit mit normaler Priorität gelaufen ist, nun eine hohe Priorität bekommen muss, da sich bestimmte Umstände (z. B. Messwerte) geändert haben. Die Zeit, die der Scheduler mit seiner Arbeit verbringt, ist sehr kurz. Das Bild zeigt die Anzahl der Zyklen, die jedes RTOS jeweils für einen Context-Switch – also den Wechsel zwischen zwei Tasks – benötigt. Auf dem Arm Cortex-M7 des Testboards, der mit 72 MHz getaktet wurde, bedeutet dies eine Ausführungszeit von etwa 13,9 ns pro Zyklus. Zephyr schneidet hier mit durchschnittlich 265 Zyklen, also etwa 3,7 μs pro Context-Switch, am besten ab. Es soll aber betont werden, dass diese Werte lediglich die grobe Richtung angeben. Sie hängen stark von der konkreten Implementierung, der Architektur, der Compiler-Toolchain und weiteren Faktoren ab.
FreeRTOS versteht seine Aufgabe als Echtzeitbetriebssystem recht minimalistisch: Die Kernkompetenzen beschränken sich auf einen Scheduler, Inter-Task-Kommunikation sowie Mutexe bzw. Semaphoren. Anders als bei Zephyr gibt es nur wenige mitgelieferte APIs, die den Zugriff auf die Hardware abstrahieren. Wenn also von einem STM32-Board eine Nachricht über UART gesendet werden soll, bietet es sich mit FreeRTOS an, die STM32-HAL oder eine andere Treiberimplementierung für UART aufzurufen. Zephyr und RIOT OS hingegen liefern UART-Treiber (und Treiber für viele andere Peripherien) inklusive API mit. Hier sticht Zephyr heraus: Die Liste an unterstützten Peripherien, Kommunikationsprotokollen und nützlichen APIs ist lang, von einer generischen Sensor-API über ein eigenes State-Machine-Framework bis hin zu einer USB-C-Library. Auch RIOT OS verfügt über eine wachsende List von Treibern, bleibt aber hinter der Vielfalt von Zephyr zurück.
Nicht selten werden Embedded-Systeme mit Batterien betrieben, was spezielle Anforderungen an den Energieverbrauch der verwendeten Mikrocontroller nach sich zieht. Fast alle Chip-Hersteller bieten spezielle Low-Power-Varianten an oder integrieren zumindest die Funktion, den Mikrocontroller in einen energiesparenden Zustand (Sleep-Mode) zu versetzen. Die drei vorgestellten RTOS können sich diesen Umstand zunutze machen, indem sie während der Idle-Task in den Schlafzustand wechseln. Allein Zephyr bietet darüber hinaus ein eigenes Power-Management-Subsystem, mit dem Konfigurationsmöglichkeiten wie Power-Gating (Unterbrechung der Stromzufuhr zu bestimmten Teilen des Chips) und Device-Power-Management (Ausschalten oder Schlafenlegen nicht benötigter Peripheriegeräte) ermöglicht werden. Zusätzlich dazu kann der Kernel von FreeRTOS und Zephyr als tickless konfiguriert werden. Hierbei werden die periodischen Interrupts, nach denen der Scheduler seine Arbeit verrichtet, ausgesetzt und der Kernel kann nur durch Events aufgeweckt werden, womit sich der Stromverbrauch noch einmal reduziert. RIOT OS besitzt ausschließlich einen Tickless-Kernel, was beim Design der Applikation beachtet werden muss.
Oft ist es nötig, Updates auf ein Embedded-System aufzuspielen, um zum Beispiel die Funktionalität zu erweitern oder Sicherheits-Patches zu installieren. Damit der Update-Prozess reibungslos und vor allem sicher stattfindet, kann ein Bootloader eingesetzt werden. Immer häufiger wird es nötig, dass ein Bootloader den Programmcode nicht einfach nur ausführt, sondern vor dem Start auch die Integrität des Image verifiziert. Damit können Änderungen am Programm, die z. B. durch einen korrumpierten Flash-Speicher zufällig oder z. B. durch einen Hacker-Angriff mutwillig entstanden sind, erkannt und dementsprechend weitere Schritte eingeleitet werden. Außerdem kann ein Bootloader den Update-Prozess überwachen und auch absichern, sodass kein Angreifer von außen ein ungewolltes Update einspielen kann. Sollte ein neu aufgespieltes Image nicht laden, verfügen viele Bootloader auch über die Möglichkeit, wieder auf den alten Stand zu wechseln. Eine beliebte Implementierung eines sicheren Bootloaders ist MCUBoot. Dieser wird von FreeRTOS, Zephyr wie auch RIOT unterstützt.
Mit Blick auf Multicore-Prozessoren wie den ESP32 bringen sowohl FreeRTOS als auch Zephyr Symmetric-Multiprocessing(SMP)-Funktionalitäten mit, mit denen Tasks auf mehrere Kerne verteilt werden können. Beide unterstützen APIs zur Synchronisierung von Tasks zwischen mehreren Prozessoren, wobei auch hier Zephyr eindeutig die Nase vorne hat, was die Fülle an API-Funktionen und deren Dokumentation angeht. RIOT OS hingegen unterstützt kein SMP.
Bei der Gestaltung eines Systems kann es auch zielführend sein, einen Schritt zurückzutreten und sich zu fragen: Ist ein RTOS überhaupt nötig oder lässt sich die Aufgabe auch auf Bare Metal, also durch die direkte Programmierung des Mikrocontrollers ohne Betriebssystem bewältigen? Bei Systemen mit stark begrenztem Flash-Speicher und/oder Arbeitsspeicher kann es vorkommen, dass selbst ein noch so kleiner RTOS-Kernel den Platz aufbraucht, der eigentlich für den Applikationscode benötigt wird. Hier bleibt dann nur die Bare-Metal-Implementierung, die durch den weggefallenen Overhead eines Betriebssystems in den meisten Fällen auch schneller läuft. Auf der anderen Seite sollte ebenfalls geprüft werden, ob nicht ein mächtigeres Betriebssystem wie Linux die Entwicklung durch zahlreiche existierende Werkzeuge und Programme vereinfacht. Voraussetzung dafür ist ein geeigneter Prozessor mit meist höheren Kosten und Platzverbrauch und dass keine Echtzeitanforderungen vorliegen.
Klar ist: Nicht jede Applikation braucht zwingend ein RTOS. Aber jede durch ein RTOS mitgebrachte Funktion wie Multitasking, Queues, Timer erleichtert meistens die Entwicklung, selbst wenn die Anwendung auch ohne OS auskommen könnte.
Letztendlich kommt es ganz auf die Applikation an, welches (Real-Time) OS am besten geeignet ist, oder ob vielleicht sogar eine Bare-Metal-Programmierung ausreicht. Deswegen sollte zuerst die Software-Architektur stehen, auf Basis derer die Eignung eines Betriebssystems evaluiert werden kann. Grundsätzlich sollte der Einsatz eines RTOS die Implementierung einer Anwendung vereinfachen, statt die Komplexität des Projekts zu erhöhen. Werden mehrere Tasks benötigt, die stark unterschiedliche Laufzeiten haben und dabei auch noch untereinander kommunizieren müssen, bietet sich oft ein RTOS an, da sonst der Weg einer aufwendigen Eigenimplementierung (möglicherweise sogar mit einem selbst geschriebenen Scheduler) gegangen werden muss.
Die Zielarchitektur spielt natürlich auch eine Rolle: RIOT OS hat es sich zum Ziel gesetzt, neben moderneren Prozessoren auch Mikrocontroller mit 8 bzw. 16 bit zu unterstützen. FreeRTOS unterstützt zusätzlich zu gängigen Architekturen auch einige 16-bit-Controller, und Zephyr fühlt sich am wohlsten auf Chips mit 32 und sogar 64 bit. Wie bereits erwähnt, finden sich auf den Webseiten der Anbieter die Listen der unterstützten Boards und Prozessoren. Sicher ist aber, dass alle drei hier vorgestellten Echtzeitbetriebssysteme eine solide Grundlage für die Entwicklung von Multitasking-Applikationen bieten und viele Features wie I2C- und Ethernet-Treiber mitbringen, die im Embedded-Bereich immer wieder gefordert sind. (lb)