Aus Linux-Magazin 06/2008

Grundlagen: Echtzeitsysteme mit Linux

Eine Anwendung erfordert das Einhalten von Deadlines im Bereich von Milli- oder gar im Mikrosekunden? Linux bietet gleich mehrere Möglichkeiten, um derartige Echtzeitanforderungen zu erfüllen .

Echtzeit ist eine Frage der Rechtzeitigkeit, keine der Schnelligkeit. Solange das System alle zeitlichen Anforderungen, die so genannten Deadlines, einhält, spielt es keine Rolle, ob Reaktionen innerhalb einer Sekunde oder einer Millisekunde erfolgen. Trotz dieser Definition hat es sich aber eingebürgert, dass Anwender von Echtzeitsystemen Worst-Case-Latenzen im Mikro- oder zumindest im unteren Millisekundenbereich erwarten.

Unter Latenzzeit versteht man jene Zeit, die zwischen dem Auslösen eines Ereignisses (Interrupt) und dem Start der zugehörigen Interrupt-Service-Routine (Interrupt-Latenzzeit) oder des Rechenprozesses (Prozess-Latenzzeit) vergeht. Bei strenger Auslegung muss der Hersteller nachweisen, dass sein Echtzeit-Betriebssystem die von ihm angegebene Latenzzeit unter allen Umständen einhält.

Dieser Nachweis fällt allerdings (nicht nur im Linux-Umfeld) ausgesprochen schwer. Bei komplexen Betriebssystemen wird er daher weniger wissenschaftlich als vielmehr ingenieursmäßig geführt: Man setzt das System unter Höchstlast und misst die Zeiten einfach aus. Eine ausreichend lange Messzeit vorausgesetzt, wird der Worst Case, der Fall, bei dem die größten Latenzzeiten auftreten, wohl eintreten. Hoffentlich!

Da früher ein Standard-Linux-Kernel harte Echtzeitanforderungen (es gibt kein Pardon für zeitliche Ausrutscher) im Mikrosekundenbereich nicht erfüllen konnte, setzten und setzen viele Entwickler auf einen Dual-Kernel-Ansatz: Sie kombinieren den Linux-Kernel mit einem Echtzeit-Kernel (siehe Abbildung 1).

Abbildung 1: Dual-Kernel-Ansatz: Linux als Idle-Task eines Echtzeit-Kernels, der die Hardware steuert. Der Datenaustausch zwischen beiden Teilen erfolgt über Message-Queues.

Abbildung 1: Dual-Kernel-Ansatz: Linux als Idle-Task eines Echtzeit-Kernels, der die Hardware steuert. Der Datenaustausch zwischen beiden Teilen erfolgt über Message-Queues.

Zwei Herzen in der Brust

Der Echtzeit-Kernel steuert bei dieser Kombination die Hardware, insbesondere das Sperren und Freigeben von Interrupts. Damit sitzt er, logisch betrachtet, unterhalb des Linux-Kernels, und Linux wird zur Idle-Task dieses zweiten Betriebssystems. Das hat etwas von einem echtzeitfähigen Hypervisor.

Die technische Realisierung einer solchen Lösung, das Hijacken der Interrupts, ist gar nicht kompliziert. Für den Entwickler, der den zusätzlichen Echtzeit-Kernel nutzen möchte, bedeutet dies, dass er seine Software in zwei Teile aufteilt, nämlich in die Teile, die Echtzeit benötigen, und jene, die das nicht müssen. Letztere realisiert er dann typischerweise als normale Linux-Tasks. Der Datenaustausch zwischen dem echtzeit- und dem nicht echtzeitfähigen Teil passiert über Message-Queues (Fifos) oder über einen gemeinsamen Speicherbereich.

Zwei Rivalen und eine Abspaltung

In der Vergangenheit konkurrierten zwei Ansätze um die Gunst der nach Echtzeit verlangenden Entwickler: Victor Yodaiken ging mit seinem RT-Linux [1] als Erster auf den Markt. Allerdings ließ er sich seinen Echtzeit-Kernel patentieren und komplizierte damit den Einsatz von RT-Linux in kommerziellen Lösungen. Yodaiken verkaufte sein Patent und alle (Marken-) Rechte von RT-Linux im Februar 2007 an die Firma Windriver [2].

Aufgrund der in Europa verbreiteten Vorbehalte gegen das Patent und damit gegen RT-Linux hat Paolo Mantegazza von der Technischen Universität Mailand RTAI (Realtime Application Interface, [3]) entwickelt, das hierzulande starke Verbreitung gefunden hat.

Alle Bedenken, mit RTAI das Realtime-Patent von Yodaiken nicht doch zu verletzen, hat Mantegazza mit seinem Echtzeit-Kernel aber nicht ausräumen können. Erst der Technologiewechsel zu dem “Nanokernel Hardware Abstraction Layer” Adeos (Adaptive Domain Environment for Operating Systems, [4]) hat diese Diskussion beendet. Adeos bildet bis heute die Basis von RTAI.

Ähnlich wie RT-Linux definiert auch RTAI ein eigenes API-Set für den Zugriff auf die Echtzeit-Funktionalitäten. Um Entwicklern, die vorher mit Echtzeit-Betriebssystemen wie Vx Works oder P-SOS gearbeitet haben, den leichten Umstieg auf ein Linux-basiertes System zu ermöglichen, implementiert das Projekt Xenomai [5] die APIs bekannter Echtzeit-Betriebssysteme “on top of RTAI” [6]. Nach Differenzen mit den RTAI-Entwicklern hat sich Xenomai zwischenzeitlich als Fork von RTAI selbstständig gemacht.

Während es um RTAI in letzter Zeit ruhiger geworden ist, hat Xenomai eine lebendige Entwicklergemeinde. Da Xenomai den Schwerpunkt auf die Programmier-Interfaces und nicht auf die darunter liegende Echtzeittechnologie setzt, bietet das Projekt zudem noch eine Perspektive hin zu einem echtzeitfähigen Standardkernel.

Licht und Schatten

Der Mehr-Kernel-Ansatz von RT-Linux, RTAI oder Xenomai ist weniger aus Tugend als vielmehr aus der Not heraus geboren. Sicher, es leuchtet ein, dass eine Worst-Case-Analyse bei einem überschaubaren Echtzeitkernel einfacher durchzuführen ist als bei einem ausgewachsenen Linux-System. Andererseits hat der Entwickler strukturell zwei unterschiedliche Betriebssysteme mit ihren jeweils eigenen Funktionen zu bewältigen. Neben dem erhöhten Einarbeitungsaufwand fällt ein nicht zu verachtender Pflegeaufwand an. Auch sind die Möglichkeiten des Echtzeit-Kernels eingeschränkt.

Viele Subsysteme, etwa die IP-Kommunikation, stehen gar nicht oder nur eingeschränkt in Echtzeit zur Verfügung. Zudem muss der Entwickler die fragliche Applikation aufteilen in einen Teil, der den strengen zeitlichen Anforderungen genügt, und einen Teil, der ohne besondere zeitliche Anforderungen auskommt. Last but not least: Die Realzeit-Teile der Applikation laufen typisch im Kernelkontext ab und können damit leicht das komplette System kompromittieren.

So verwundert es nicht, dass sich die Entwickler bemüht haben dem Linux-Kernel selbst Echtzeit-Eigenschaften zu verabreichen. Wer bereit ist seinen Kernel zu patchen, kommt in den Genuss der Verbesserung. Wesentliche Teile der unter dem Namen RT-Preempt [7] laufenden Patches hat Torvalds zwischenzeitlich in den Kernel übernommen; sie sorgen dafür, dass Latenzzeiten in die Größenordnungen schrumpfen, die sonst nur klassischen Echtzeit-Betriebssystemen eigen sind. Dazu haben die RT-Preempt-Entwickler zunächst die Zeitverwaltung mit dem High-Resolution-Timer und dem Tickless-System auf neue Füße gestellt.

Zeit hochaufgelöst

Der High-Resolution-Timer entkoppelt zeitgesteuerte Interaktionen vom periodischen Timer-Interrupt [8]. Dieser Interrupt tritt bei Linux typischerweise alle 4 Millisekunden (250 Hertz) oder alle 10 Millisekunden (100 Hertz) auf und ist für die zeitabhängigen Vorgänge im Kernel verantwortlich. Die zugehörige Interrupt-Service-Routine ruft beispielsweise den Scheduler auf oder weckt Rechenprozesse, die sich für eine bestimmte Zeitdauer schlafen gelegt haben.

Da ein Standardkernel Interrupts aber nur zu diskreten Zeitpunkten auslöst, kommt es immer dann zu unerwünschten Latenzzeit-Verlängerungen, wenn der Interrupt nicht zufällig mit dem gewünschten Aufweckzeitpunkt zusammenfällt (siehe Abbildung 2). Der mit Kernel 2.6.21 eingeführte High-Resolution-Timer löst das Problem: Legt sich ein Rechenprozess für eine definierte Zeitdauer schlafen, sorgt der Timer dafür, dass ihn das System asynchron genau zum gewünschten Zeitpunkt weckt.

Abbildung 2: Die Linux-Kerne 2.6.20 und früher weckten Rechenprozesse immer zu diskreten Zeiten auf. Das führt zu einer unerwünschten Verlängerung der Latenzzeit.

Abbildung 2: Die Linux-Kerne 2.6.20 und früher weckten Rechenprozesse immer zu diskreten Zeiten auf. Das führt zu einer unerwünschten Verlängerung der Latenzzeit.

Wer diesen Timer auch zum Aktivieren des Scheduling und zur Aktualisierung der internen Zeitzähler verwendet, darf sogar auf den periodischen Ticker komplett verzichten. Die eingesparten Interrupts auf einem solchen Tickless-System verlängern gerade auf Notebooks und auf eingebetteten Systemen die Sleep-Zeiten und sparen so Strom.

Tabelle 1: Gemessene
Latenzzeiten

Alles wird Thread

High-Resolution-Timer und Tickless-System reichen aber nicht aus, um Latenzzeiten im Milli- oder gar Mikrosekundenbereich zu garantieren. Für die langen Latenzzeiten sind nämlich im Wesentlichen die kritischen Abschnitte verantwortlich (siehe Kasten “Schutz kritischer Abschnitte”). Im aktuellen Standardkernel 2.6.23 konnten die Entwickler einige kritische Abschnitte entfernen oder zumindest in der Dauer reduzieren. Dennoch: Die bestehenden kritischen Abschnitte verhindern die weitere Reduktion der Latenzzeit.

Schutz kritischer
Abschnitte

Kritischer Abschnitt heißt eine Codesequenz, die auf ein gemeinsam benutztes Betriebsmittel, beispielsweise eine Datenstruktur, zugreift. Wenn während des Zugriffs auf eine solche Datenstruktur eine Unterbrechung stattfindet und zwischenzeitlich eine andere Codesequenz dieselben Daten modifiziert, werden die Inhalte der Datenstruktur inkonsistent. Für die Korrektheit von Ergebnissen und die Stabilität des Systems wäre das katastrophal.

Für die ersten Unix-Systeme vor rund 30 Jahren reichte eine einfache Interrupt-Sperre zum Schutz der kritischen Abschnitte aus. Spätestens seit Aufkommen der Mehrprozessormaschinen jedoch haben Lockings auf der Interrupt-Sperre ihre Wirksamkeit verloren. Bei realer Parallelverarbeitung müsste das System die Interrupts auf sämtlichen Prozessoren sperren – für die Performance und vor allem für die Latenzzeiten ein Desaster.

Glücklicherweise lassen sich kritische Abschnitte auch anders schützen: Informatiker haben Semaphore, Mutexe, Spinlocks und andere Mechanismen aus ihrer Trickkiste gezaubert. Alle Methoden haben mit der Interrupt-Sperre gemeinsam, dass sie den Zugriff auf den kritischen Abschnitt sequenzialisieren. Einer nach dem anderen darf die Daten modifizieren, jede Codesequenz wartet, bis sie an der Reihe ist. Das hat zwar in Summe kaum Einfluss auf die Performance, durch die entstehenden Wartezeiten sehr wohl aber auf die Latenzzeit.

Semaphore und Mutexe realisieren den gegenseitigen Ausschluss (Mutual Exclusion) durch passives Warten. Hierbei legt der Kernel einen zugriffswilligen Prozess (Kernelthread) schlafen, falls gerade andere Prozesse die kritischen Daten modifizieren. Während bei einem Semaphor die Anzahl parallel zugreifender Jobs parametrierbar ist, erlaubt ein Mutex exakt einem Job den Zugriff im kritischen Abschnitt.

Will eine Codesequenz im Interrupt-Kontext (Interrupt-Service-Routine, Tasklet oder Timer) einen kurzen kritischen Abschnitt betreten, macht eine Kombination aus Interrupt-Sperre und aktivem Warten, das so genannte Spinlock, die beste Figur – hier ist es sehr effizient. Mehr zum Schutz kritischer Abschnitte verrät [11].

Doch auch hierfür gibt es Lösungen, wenngleich Linus Torvalds sie bisher noch nicht in seinen Kernel übernommen hat. Das RT-Preempt-Patch eliminiert nämlich Spinlocks und setzt voll und ganz auf Realtime-Mutexe ([9], [10]). Der damit verbundene Wechsel vom aktiven Warten aufs passives Warten (schlafen legen) ist alles andere als trivial. Schließlich lässt sich eine Interrupt-Service-Routine nicht schlafen legen.

Daher bauen die Entwickler Interrupt-Service-Routinen und auch Soft-IRQs (Tasklets und Timer) zu hoch priorisierten Kernelthreads um, die dann sehr wohl schlafen gehen können (siehe Abbildung 3). Aufgrund ihrer hohen Priorität arbeitet das System die ehemaligen ISRs weiterhin vor den übrigen Rechenprozessen ab. Echtzeit-kritischen Rechenprozessen lassen sich die ISRs aber durchaus unterordnen.

Nachteil der “Alles ist ein Thread”-Lösung: Bereits bei einem Interrupt ist ein Prozesswechsel (allerdings nicht in den User-Kontext) notwendig. Das ist jedoch nur auf schwächeren Prozessoren spürbar. Bei leistungsstarken Prozessoren fällt der Switch kaum ins Gewicht [12].

Abbildung 3: Interrupt-Service-Routinen und Soft-IRQs mutieren im gepatchten Kernel zu Threads, was sie unterbrechbar macht: Ein Echtzeitprozess, der auf der User-Ebene läuft, kann sie verdrängen.

Abbildung 3: Interrupt-Service-Routinen und Soft-IRQs mutieren im gepatchten Kernel zu Threads, was sie unterbrechbar macht: Ein Echtzeitprozess, der auf der User-Ebene läuft, kann sie verdrängen.

Zeit für Mikrosekunden

Dank dieser Maßnahmen ist Linux jetzt auf aktueller Hardware bezüglich seiner Latenzzeiten im mittleren bis unteren Mikrosekundenbereich angekommen (siehe Kasten “Echtzeit-Linux in Zahlen”). Doch das ist noch nicht alles: Auf Mehrkern- respektive Mehrprozessorsystemen kann der Entwickler beispielsweise Echtzeitaufgaben einzelnen Prozessoren zuordnen (siehe Abbildung 4). Man spricht in diesem Fall von der CPU-Affinität eines Rechenprozesses.

Isolation – sie ist beim Booten einzustellen – verhindert außerdem, dass die für Echtzeitaufgaben reservierte CPU andere Rechenprozesse und auch Interrupt-Service-Routinen abarbeitet – und fertig ist ein relativ deterministisches Echtzeitsystem auf der Basis eines Standard-Linux-Systems ohne zusätzlichen Realtime-Kernel. (Heike Jurzik, jk)

Echtzeit-Linux in
Zahlen

Viele beurteilen die Qualitäten eines Echtzeit-Betriebssystems anhand der Interrupt- und Task-Latenzzeiten. Latenzzeiten hängen allerdings stark von der verwendeten Hardware ab, insbesondere dem Prozessor, der Systemkonfiguration und vor allem auch von der Messmethode. Die Zahlen aus Tabelle 1 stammen aus dem Artikel [12]. Der dort beschriebene Jitter lässt sich vereinfachend als (Task-)Latenzzeit interpretieren. Messungen der Autoren und weitere Ergebnisse [13] bestätigen die Größenordnungen.

Infos

[1] RT-Linux: [http://www.rtlinuxfree.com]

[2] Windriver: [http://www.windriver.com/de]

[3] RTAI: [https://www.rtai.org]

[4] Adeos: [http://home.gna.org/adeos/]

[5] Xenomai: [http://www.xenomai.org]

[6] Sebastian Smolorz, “Echtzeit-Linux mit Xenomai”: “Elektronik” 3/2007 sowie [http://fpga-linux.com/fileadmin/emlix/dokumente/FA_Xenomai.pdf]

[7] RT-Preempt: [http://www.kernel.org/pub/linux/kernel/projects/rt/]

[8] Eva-Katharina Kunst, Jürgen Quade, “Kern-Technik”, Folge 31: Linux-Magazin 01/07, S. 110

[9] Eva-Katharina Kunst, Jürgen Quade, “Kern-Technik”, Folge 34: Linux-Magazin 07/07, S. 102

[10] Linux-Realtime-Wiki: [http://rt.wiki.kernel.org/index.php/Main_Page]

[11] Eva-Katharina Kunst, Jürgen Quade, “Linux-Treiber entwickeln”: Dpunkt-Verlag, 2. Auflage, 2006

[12] Arthur, Emde, Mc Guire, “Assesment of the Realtime Preemption Patches (RT-PREEMPT) and their impact on the general performance of the system”: [http://linuxdevices.com/files/article081/Siro.pdf]

[13] Ergebnisse von Latenzzeit-Messungen: [http://rt.wiki.kernel.org/index.php/CONFIG_PREEMPT_RT_Patch]

Die Autoren

Eva-Katharina Kunst, Journalistin, und Jürgen Quade, Professor an der Hochschule Niederrhein, sind seit den Anfängen von Linux Fans von Open Source. Unter dem Titel “Linux-Treiber entwickeln” [11] haben sie zusammen ein Buch zum Kernel 2.6 veröffentlicht.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben